diff options
186 files changed, 7013 insertions, 3206 deletions
diff --git a/.gitignore b/.gitignore index 85ddf3871f..4169a2d96c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,12 +13,19 @@ autom4te.cache/ config.log config.status configure -src/bitcoin-config.h -src/bitcoin-config.h.in +src/config/bitcoin-config.h +src/config/bitcoin-config.h.in +src/config/stamp-h1 src/build-aux/ -src/stamp-h1 share/setup.nsi share/qt/Info.plist +# Libtool +libtool +src/m4/libtool.m4 +src/m4/ltoptions.m4 +src/m4/ltsugar.m4 +src/m4/ltversion.m4 +src/m4/lt~obsolete.m4 src/qt/*.moc src/qt/moc_*.cpp @@ -1,4 +1,4 @@ -Copyright (c) 2009-2013 Bitcoin Developers +Copyright (c) 2009-2014 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile.am b/Makefile.am index 719af42ac7..3a6a6b6d88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,7 @@ COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ dist-hook: -$(MAKE) -C $(top_distdir)/src/leveldb clean + -$(MAKE) -C $(top_distdir)/src/secp256k1 distclean -$(GIT) archive --format=tar HEAD -- src/version.cpp | $(AMTAR) -C $(top_distdir) -xf - distcheck-hook: @@ -1,7 +1,7 @@ Bitcoin Core integration/staging tree ===================================== -http://www.bitcoin.org +https://www.bitcoin.org Copyright (c) 2009-2014 Bitcoin Core Developers @@ -15,7 +15,7 @@ out collectively by the network. Bitcoin Core is the name of open source software which enables the use of this currency. For more information, as well as an immediately useable, binary version of -the Bitcoin Core software, see http://www.bitcoin.org/en/download. +the Bitcoin Core software, see https://www.bitcoin.org/en/download. License ------- @@ -76,12 +76,14 @@ Translations Changes to translations as well as new translations can be submitted to [Bitcoin Core's Transifex page](https://www.transifex.com/projects/p/bitcoin/). -Periodically the translations are pulled from Transifex and merged into the git repository. See the +Translations are periodically pulled from Transifex and merged into the git repository. See the [translation process](doc/translation_process.md) for details on how this works. -**Important**: We do not accept translation changes as github pull request because the next +**Important**: We do not accept translation changes as GitHub pull requests because the next pull from Transifex would automatically overwrite them again. +Translators should also subscribe to the [mailing list](https://groups.google.com/forum/#!forum/bitcoin-translators). + Development tips and tricks --------------------------- diff --git a/autogen.sh b/autogen.sh index 5b883a6a4c..50b85bcba0 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,4 +2,7 @@ set -e srcdir="$(dirname $0)" cd "$srcdir" +if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then + export LIBTOOLIZE="${GLIBTOOLIZE}" +fi autoreconf --install --force diff --git a/configure.ac b/configure.ac index d8521ad3ad..d3500b4d93 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,7 @@ define(_COPYRIGHT_YEAR, 2014) AC_INIT([Bitcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[info@bitcoin.org],[bitcoin]) AC_CONFIG_AUX_DIR([src/build-aux]) AC_CONFIG_MACRO_DIR([src/m4]) +LT_INIT([disable-shared]) AC_CANONICAL_HOST AH_TOP([#ifndef BITCOIN_CONFIG_H]) AH_TOP([#define BITCOIN_CONFIG_H]) @@ -111,7 +112,7 @@ AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[spec AC_CONFIG_SRCDIR([src]) -AC_CONFIG_HEADERS([src/bitcoin-config.h]) +AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) dnl Checks for programs. AC_PROG_CXX @@ -192,10 +193,13 @@ case $host in AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) - AX_CHECK_LINK_FLAG([[-static]],[LDFLAGS="$LDFLAGS -static"]) AX_CHECK_LINK_FLAG([[-static-libgcc]],[LDFLAGS="$LDFLAGS -static-libgcc"]) AX_CHECK_LINK_FLAG([[-static-libstdc++]],[LDFLAGS="$LDFLAGS -static-libstdc++"]) + # -static is interpreted by libtool, where it has a different meaning. + # In libtool-speak, it's -all-static. + AX_CHECK_LINK_FLAG([[-static]],[LDFLAGS="$LDFLAGS -static"; LIBTOOL_LDFLAGS="$LIBTOOL_LDFLAGS -all-static"]) + AC_PATH_PROG([MAKENSIS], [makensis], none) if test x$MAKENSIS = xnone; then AC_MSG_WARN("makensis not found. Cannot create installer.") @@ -368,7 +372,14 @@ if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) fi -AC_CHECK_HEADERS([stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h]) +AC_CHECK_HEADERS([endian.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h]) +AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])]) +AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])]) + +AC_CHECK_DECLS([le32toh, le64toh, htole32, htole64, be32toh, be64toh, htobe32, htobe64],,, + [#if HAVE_ENDIAN_H + #include <endian.h> + #endif]) dnl Check for MSG_NOSIGNAL AC_MSG_CHECKING(for MSG_NOSIGNAL) @@ -674,6 +685,7 @@ AM_CONDITIONAL([USE_LCOV],[test x$use_lcov == xyes]) AM_CONDITIONAL([USE_COMPARISON_TOOL],[test x$use_comparison_tool != xno]) AM_CONDITIONAL([USE_COMPARISON_TOOL_REORG_TESTS],[test x$use_comparison_tool_reorg_test != xno]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) +AM_CONDITIONAL([USE_LIBSECP256K1],[test x$use_libsecp256k1 = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -689,6 +701,7 @@ AC_SUBST(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE) AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR) +AC_SUBST(LIBTOOL_LDFLAGS) AC_SUBST(USE_UPNP) AC_SUBST(USE_QRCODE) AC_SUBST(INCLUDES) diff --git a/contrib/bitrpc/bitrpc.py b/contrib/bitrpc/bitrpc.py index a84d7e34dd..02577b1b6a 100644 --- a/contrib/bitrpc/bitrpc.py +++ b/contrib/bitrpc/bitrpc.py @@ -22,6 +22,18 @@ if cmd == "backupwallet": print access.backupwallet(path) except: print "\n---An error occurred---\n" + +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: + print "\n---An error occurred---\n" elif cmd == "getaccount": try: diff --git a/contrib/debian/control b/contrib/debian/control index 9e006a7070..a04e88d4e1 100644 --- a/contrib/debian/control +++ b/contrib/debian/control @@ -6,6 +6,7 @@ Uploaders: Micah Anderson <micah@debian.org> Build-Depends: debhelper, devscripts, automake, + libtool, bash-completion, libboost-system-dev (>> 1.35) | libboost-system1.35-dev, libdb4.8++-dev, diff --git a/contrib/debian/manpages/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1 index cd478b1875..25a423f9c4 100644 --- a/contrib/debian/manpages/bitcoin-qt.1 +++ b/contrib/debian/manpages/bitcoin-qt.1 @@ -139,6 +139,9 @@ Execute command when the best block changes (%s in cmd is replaced by block hash \fB\-walletnotify=\fR<cmd> Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) .TP +\fB\-respendnotify=\fR<cmd> +Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID) +.TP \fB\-alertnotify=\fR<cmd> Execute command when a relevant alert is received (%s in cmd is replaced by message) .TP diff --git a/contrib/gitian-descriptors/gitian-osx-bitcoin.yml b/contrib/gitian-descriptors/gitian-osx-bitcoin.yml index 44b5de9be3..bc3d561c35 100644 --- a/contrib/gitian-descriptors/gitian-osx-bitcoin.yml +++ b/contrib/gitian-descriptors/gitian-osx-bitcoin.yml @@ -11,6 +11,7 @@ packages: - "bsdmainutils" - "pkg-config" - "p7zip-full" +- "libtool" reference_datetime: "2013-06-01 00:00:00" remotes: diff --git a/contrib/verifysfbinaries/README.md b/contrib/verifysfbinaries/README.md index f646d1efd1..8c038865bd 100644 --- a/contrib/verifysfbinaries/README.md +++ b/contrib/verifysfbinaries/README.md @@ -1,5 +1,5 @@ ### Verify SF Binaries ### -This script attempts to download the signature file `SHA256SUMS.asc` from SourceForge. +This script attempts to download the signature file `SHA256SUMS.asc` from https://bitcoin.org. It first checks if the signature passes, and then downloads the files specified in the file, and checks if the hashes of these files match those that are specified in the signature file. diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh index e92295661c..3eb4693883 100755 --- a/contrib/verifysfbinaries/verify.sh +++ b/contrib/verifysfbinaries/verify.sh @@ -1,6 +1,6 @@ #!/bin/bash -### This script attempts to download the signature file SHA256SUMS.asc from SourceForge +### This script attempts to download the signature file SHA256SUMS.asc from bitcoin.org ### It first checks if the signature passes, and then downloads the files specified in ### the file, and checks if the hashes of these files match those that are specified ### in the signature file. @@ -18,11 +18,11 @@ WORKINGDIR="/tmp/bitcoin" TMPFILE="hashes.tmp" #this URL is used if a version number is not specified as an argument to the script -SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.9.0rc1/SHA256SUMS.asc" +SIGNATUREFILE="https://bitcoin.org/bin/0.9.2.1/SHA256SUMS.asc" SIGNATUREFILENAME="SHA256SUMS.asc" RCSUBDIR="test/" -BASEDIR="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/" +BASEDIR="https://bitcoin.org/bin/" VERSIONPREFIX="bitcoin-" RCVERSIONSTRING="rc" @@ -62,7 +62,7 @@ WGETOUT=$(wget -N "$BASEDIR$SIGNATUREFILENAME" 2>&1) #and then see if wget completed successfully if [ $? -ne 0 ]; then echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?" - echo "[bitcoin-]<version>-[rc[0-9]] (example: bitcoin-0.7.1-rc1)" + echo "[bitcoin-]<version>-[rc[0-9]] (example: bitcoin-0.9.2-rc1)" echo "wget output:" echo "$WGETOUT"|sed 's/^/\t/g' exit 2 diff --git a/doc/build-osx.md b/doc/build-osx.md index 0de5c792e9..bc42723b12 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -22,7 +22,7 @@ Xcode 4.3 or later, you'll need to install its command line tools. This can be done in `Xcode > Preferences > Downloads > Components` and generally must be re-done or updated every time Xcode is updated. -There's an assumption that you already have `git` installed, as well. If +There's also an assumption that you already have `git` installed. If not, it's the path of least resistance to install [Github for Mac](https://mac.github.com/) (OS X 10.7+) or [Git for OS X](https://code.google.com/p/git-osx-installer/). It is also @@ -30,11 +30,8 @@ available via Homebrew or MacPorts. You will also need to install [Homebrew](http://brew.sh) or [MacPorts](https://www.macports.org/) in order to install library -dependencies. It's largely a religious decision which to choose, but, as of -December 2012, MacPorts is a little easier because you can just install the -dependencies immediately - no other work required. If you're unsure, read -the instructions through first in order to assess what you want to do. -Homebrew is a little more popular among those newer to OS X. +dependencies. It's largely a religious decision which to choose, however, Homebrew +is now used for building release versions. The installation of the actual dependencies is covered in the Instructions sections below. @@ -44,9 +41,7 @@ Instructions: MacPorts ### Install dependencies -Installing the dependencies using MacPorts is very straightforward. - - sudo port install boost db48@+no_java openssl miniupnpc autoconf pkgconfig automake + sudo port install boost db48@+no_java openssl miniupnpc autoconf pkgconfig automake libtool Optional: install Qt4 @@ -74,13 +69,13 @@ Instructions: Homebrew #### Install dependencies using Homebrew - brew install autoconf automake berkeley-db4 boost miniupnpc openssl pkg-config protobuf qt + brew install autoconf automake libtool berkeley-db4 boost miniupnpc openssl pkg-config protobuf qt Note: After you have installed the dependencies, you should check that the Homebrew installed version of OpenSSL is the one available for compilation. You can check this by typing openssl version -into Terminal. You should see OpenSSL 1.0.1f 6 Jan 2014. +into Terminal. You should see OpenSSL 1.0.1h 5 Jun 2014. If not, you can ensure that the Homebrew OpenSSL is correctly linked by running @@ -103,7 +98,7 @@ PATH. ./configure make -3. It is a good idea to build and run the unit tests, too: +3. It is also a good idea to build and run the unit tests: make check @@ -131,7 +126,7 @@ For MacPorts, that means editing your macports.conf and setting ... and then uninstalling and re-installing, or simply rebuilding, all ports. As of December 2012, the `boost` port does not obey `macosx_deployment_target`. -Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` +Download `https://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` for a fix. Once dependencies are compiled, see release-process.md for how the Bitcoin-Qt.app @@ -149,13 +144,14 @@ commands: echo -e "rpcuser=bitcoinrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf" -When next you run it, it will start downloading the blockchain, but it won't +The next time you run it, it will start downloading the blockchain, but it won't output anything while it's doing this. This process may take several hours; you can monitor its process by looking at the debug.log file, like this: tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log Other commands: +------- ./bitcoind -daemon # to start the bitcoin daemon. ./bitcoin-cli --help # for a list of command-line options. diff --git a/doc/build-unix.md b/doc/build-unix.md index 1d75c206e5..0f381d56c5 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -61,10 +61,8 @@ Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: - sudo apt-get install build-essential - sudo apt-get install libtool autotools-dev autoconf - sudo apt-get install libssl-dev - + sudo apt-get install build-essential libtool autotools-dev autoconf pkg-config libssl-dev + for Ubuntu 12.04 and later: sudo apt-get install libboost-all-dev @@ -93,10 +91,9 @@ To enable the change run sudo apt-get update -for other Ubuntu & Debian: +for other Debian & Ubuntu (with ppa): - sudo apt-get install libdb4.8-dev - sudo apt-get install libdb4.8++-dev + sudo apt-get install libdb4.8-dev libdb4.8++-dev Optional: diff --git a/doc/coding.md b/doc/coding.md index 69388c9ce2..2f332e92f0 100644 --- a/doc/coding.md +++ b/doc/coding.md @@ -4,44 +4,65 @@ Coding Please be consistent with the existing coding style. Block style: - - bool Function(char* psz, int n) - { - // Comment summarising what this section of code does - for (int i = 0; i < n; i++) - { - // When something fails, return early - if (!Something()) - return false; - ... - } - - // Success return is usually at the end - return true; - } - +```c++ + bool Function(char* psz, int n) + { + // Comment summarising what this section of code does + for (int i = 0; i < n; i++) + { + // When something fails, return early + if (!Something()) + return false; + ... + } + + // Success return is usually at the end + return true; + } +``` - ANSI/Allman block style - 4 space indenting, no tabs - No extra spaces inside parenthesis; please don't do ( this ) - No space after function names, one space after if, for and while +- Includes need to be ordered alphabetically, separate own and foreign headers with a new-line (example key.cpp): +```c++ +#include "key.h" + +#include "crypto/sha2.h" +#include "util.h" +#include <openssl/foo.h> +``` +- Class or struct keywords in header files need to be ordered alphabetically: +```c++ +class CAlpha; +class CBeta; +``` +- When using namespace keyword use the following form: +```c++ +namespace Foo { + +... + +} // Foo +``` Variable names begin with the type in lowercase, like nSomeVariable. Please don't put the first word of the variable name in lowercase like someVariable. Common types: - n integer number: short, unsigned short, int, unsigned int, int64, uint64, sometimes char if used as a number - d double, float - f flag - hash uint256 - p pointer or array, one p for each level of indirection - psz pointer to null terminated string - str string object - v vector or similar list objects - map map or multimap - set set or multiset - bn CBigNum + n integer number: short, unsigned short, int, unsigned int, int64, uint64, sometimes char if used as a number + d double, float + f flag + hash uint256 + p pointer or array, one p for each level of indirection + psz pointer to null terminated string + str string object + v vector or similar list objects + map map or multimap + set set or multiset + bn CBigNum Doxygen comments ----------------- diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 378a45eda5..b356a5d88d 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -4,8 +4,8 @@ Gitian building *Setup instructions for a gitian build of Bitcoin using a Debian VM or physical system.* Gitian is the deterministic build process that is used to build the Bitcoin -Core executables [1]. It provides a way to be reasonably sure that the -executables are really built from source on github. It also makes sure that +Core executables. It provides a way to be reasonably sure that the +executables are really built from source on GitHub. It also makes sure that the same, tested dependencies are used and statically built into the executable. Multiple developers build the source code by following a specific descriptor @@ -17,9 +17,6 @@ More independent gitian builders are needed, which is why I wrote this guide. It is preferred to follow these steps yourself instead of using someone else's VM image to avoid 'contaminating' the build. -[1] For all platforms except for MacOSX, at this point. Work for deterministic -builds for Mac is under way here: https://github.com/theuni/osx-cross-depends . - Table of Contents ------------------ @@ -38,7 +35,7 @@ Preparing the Gitian builder host --------------------------------- The first step is to prepare the host environment that will be used to perform the Gitian builds. -In this guide it is explained how to set up the environment, and how to get the builds started. +This guide explains how to set up the environment, and how to start the builds. Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available. Any kind of virtualization can be used, for example: @@ -134,7 +131,7 @@ and proceed, just press `Enter`. To select a different button, press `Tab`. ![](gitian-building/debian_install_5_configure_the_network.png) -- Choose a root password and enter it twice (and remember it for later) +- Choose a root password and enter it twice (remember it for later) ![](gitian-building/debian_install_6a_set_up_root_password.png) @@ -143,7 +140,7 @@ and proceed, just press `Enter`. To select a different button, press `Tab`. ![](gitian-building/debian_install_7_set_up_user_fullname.png) ![](gitian-building/debian_install_8_set_up_username.png) -- Choose a user password and enter it twice (and remember it for later) +- Choose a user password and enter it twice (remember it for later) ![](gitian-building/debian_install_9_user_password.png) @@ -236,7 +233,7 @@ adduser debian sudo When you get a colorful screen with a question about the 'LXC directory', just go with the default (`/var/lib/lxc`). -Then set up LXC and the rest with the following is a complex jumble of settings and workarounds: +Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds: ```bash # the version of lxc-start in Debian 7.4 needs to run as root, so make sure @@ -280,7 +277,7 @@ cd .. **Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. -Clone the git repositories for bitcoin and gitian and then checkout the bitcoin version that you are willing to build. +Clone the git repositories for bitcoin and gitian and then checkout the bitcoin version that you want to build. ```bash git clone https://github.com/devrandom/gitian-builder.git @@ -319,10 +316,10 @@ you will find a list of `wget` commands that can be executed to get the dependen I needed to add `--no-check-certificate` to the OpenSSL wget line to make it work. Likely this is because the ca-certificates in Debian 7.4 is fairly old. This does not create a -security issue as the gitian descriptors check integrity of the input archives and refuse to work +security issue as the gitian descriptors check the integrity of the input archives and refuse to work if any one is corrupted. -After downloading the archives, execute the `gbuild` commends to build the dependencies. +After downloading the archives, execute the `gbuild` commands to build the dependencies. This can take a long time, but only has to be done when the dependencies change, for example to upgrade the used version. @@ -339,7 +336,7 @@ tail -f var/build.log Building Bitcoin ---------------- -To build Bitcoin (for Linux and/or Windows) just follow the steps under 'perform +To build Bitcoin (for Linux, OSX and Windows) just follow the steps under 'perform gitian builds' in [doc/release-process.md](release-process.md) in the bitcoin repository. Output from `gbuild` will look something like @@ -372,7 +369,7 @@ can be inspected in `var/install.log` and `var/build.log`. Building an alternative repository ----------------------------------- -If you want to do a test build of a pull on github it can be useful to point +If you want to do a test build of a pull on GitHub it can be useful to point the gitian builder at an alternative repository, using the same descriptors and inputs. @@ -382,13 +379,14 @@ URL=https://github.com/laanwj/bitcoin.git COMMIT=2014_03_windows_unicode_path ./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml ./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml +./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml ``` Signing externally ------------------- -If you want to do the PGP signing on another device that's possible too; just define `SIGNER` as mentioned -and follow the steps in the build process as normally. +If you want to do the PGP signing on another device that's also possible; just define `SIGNER` as mentioned +and follow the steps in the build process as normal. gpg: skipped "laanwj": secret key not available @@ -396,8 +394,9 @@ When you execute `gsign` you will get an error from GPG, which can be ignored. C in `gitian.sigs` to your signing machine and do ```bash - gpg --detach-sign ${VERSION}/${SIGNER}/bitcoin-build.assert + gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-build.assert gpg --detach-sign ${VERSION}-win/${SIGNER}/bitcoin-build.assert + gpg --detach-sign ${VERSION}-osx/${SIGNER}/bitcoin-build.assert ``` This will create the `.sig` files that can be committed together with the `.assert` files to assert your @@ -407,5 +406,5 @@ Uploading signatures --------------------- After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the -[bitcoin/gitian.sigs](https://github.com/bitcoin/gitian.sigs/) repository, or if not possible create a pull +[bitcoin/gitian.sigs](https://github.com/bitcoin/gitian.sigs/) repository, or if that's not possible create a pull request. You can also mail the files to me (laanwj@gmail.com) and I'll commit them. diff --git a/doc/release-notes.md b/doc/release-notes.md index 9272d427cd..66059800b6 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,6 +1,26 @@ (note: this is a temporary file, to be added-to by anybody, and moved to release-notes at release time) +Transaction fee changes +======================= + +This release automatically estimates how high a transaction fee (or how +high a priority) transactions require to be confirmed quickly. The default +settings will create transactions that confirm quickly; see the new +'txconfirmtarget' setting to control the tradeoff between fees and +confirmation times. + +Prior releases used hard-coded fees (and priorities), and would +sometimes create transactions that took a very long time to confirm. + + +New Command Line Options +======================== + +-txconfirmtarget=n : create transactions that have enough fees (or priority) +so they are likely to confirm within n blocks (default: 1). This setting +is over-ridden by the -paytxfee option. + New RPC methods =============== @@ -19,3 +39,49 @@ estimate. Statistics used to estimate fees and priorities are saved in the data directory in the 'fee_estimates.dat' file just before program shutdown, and are read in at startup. + +Double-Spend Relay and Alerts +============================= +VERY IMPORTANT: *It has never been safe, and remains unsafe, to rely* +*on unconfirmed transactions.* + +Relay +----- +When an attempt is seen on the network to spend the same unspent funds +more than once, it is no longer ignored. Instead, it is broadcast, to +serve as an alert. This broadcast is subject to protections against +denial-of-service attacks. + +Wallets and other bitcoin services should alert their users to +double-spends that affect them. Merchants and other users may have +enough time to withhold goods or services when payment becomes +uncertain, until confirmation. + +Bitcoin Core Wallet Alerts +-------------------------- +The Bitcoin Core wallet now makes respend attempts visible in several +ways. + +If you are online, and a respend affecting one of your wallet +transactions is seen, a notification is immediately issued to the +command registered with `-respendnotify=<cmd>`. Additionally, if +using the GUI: + - An alert box is immediately displayed. + - The affected wallet transaction is highlighted in red until it is + confirmed (and it may never be confirmed). + +A `respendsobserved` array is added to `gettransaction`, `listtransactions`, +and `listsinceblock` RPC results. + +Warning +------- +*If you rely on an unconfirmed transaction, these change do VERY* +*LITTLE to protect you from a malicious double-spend, because:* + + - You may learn about the respend too late to avoid doing whatever + you were being paid for + - Using other relay rules, a double-spender can craft his crime to + resist broadcast + - Miners can choose which conflicting spend to confirm, and some + miners may not confirm the first acceptable spend they see + diff --git a/doc/release-notes/release-notes-0.9.2.1.md b/doc/release-notes/release-notes-0.9.2.1.md new file mode 100644 index 0000000000..3168ad1a5a --- /dev/null +++ b/doc/release-notes/release-notes-0.9.2.1.md @@ -0,0 +1,207 @@ +Bitcoin Core version 0.9.2.1 is now available from: + + https://bitcoin.org/bin/0.9.2.1/ + +This is a new minor version release, bringing mostly bug fixes and some minor +improvements. OpenSSL has been updated because of a security issue (CVE-2014-0224). +Upgrading to this release is recommended. + +Please report bugs using the issue tracker at github: + + https://github.com/bitcoin/bitcoin/issues + +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). + +If you are upgrading from version 0.7.2 or earlier, the first time you run +0.9.2.1 your blockchain files will be re-indexed, which will take anywhere from +30 minutes to several hours, depending on the speed of your machine. + +Downgrading warnings +-------------------- + +The 'chainstate' for this release is not always compatible with previous +releases, so if you run 0.9.x and then decide to switch back to a +0.8.x release you might get a blockchain validation error when starting the +old release (due to 'pruned outputs' being omitted from the index of +unspent transaction outputs). + +Running the old release with the -reindex option will rebuild the chainstate +data structures and correct the problem. + +Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan +the blockchain for missing spent coins, which will take a long time (tens +of minutes on a typical machine). + +Important changes +================== + +Gitian OSX build +----------------- + +The deterministic build system that was already used for Windows and Linux +builds is now used for OSX as well. Although the resulting executables have +been tested quite a bit, there could be possible regressions. Be sure to report +these on the Github bug tracker mentioned above. + +Compatibility of Linux build +----------------------------- + +For Linux we now build against Qt 4.6, and filter the symbols for libstdc++ and glibc. +This brings back compatibility with + +- Debian 6+ / Tails +- Ubuntu 10.04 +- CentOS 6.5 + +0.9.2 - 0.9.2.1 Release notes +======================= + +The OpenSSL dependency in the gitian builds has been upgraded to 1.0.1h because of CVE-2014-0224. + +RPC: + +- Add `getwalletinfo`, `getblockchaininfo` and `getnetworkinfo` calls (will replace hodge-podge `getinfo` at some point) +- Add a `relayfee` field to `getnetworkinfo` +- Fix RPC related shutdown hangs and leaks +- Always show syncnode in `getpeerinfo` +- `sendrawtransaction`: report the reject code and reason, and make it possible to re-send transactions that are already in the mempool +- `getmininginfo` show right genproclimit + +Command-line options: + +- Fix `-printblocktree` output +- Show error message if ReadConfigFile fails + +Block-chain handling and storage: + +- Fix for GetBlockValue() after block 13,440,000 (BIP42) +- Upgrade leveldb to 1.17 + +Protocol and network code: + +- Per-peer block download tracking and stalled download detection +- Add new DNS seed from bitnodes.io +- Prevent socket leak in ThreadSocketHandler and correct some proxy related socket leaks +- Use pnode->nLastRecv as sync score (was the wrong way around) + +Wallet: + +- Make GetAvailableCredit run GetHash() only once per transaction (performance improvement) +- Lower paytxfee warning threshold from 0.25 BTC to 0.01 BTC +- Fix importwallet nTimeFirstKey (trigger necessary rescans) +- Log BerkeleyDB version at startup +- CWallet init fix + +Build system: + +- Add OSX build descriptors to gitian +- Fix explicit --disable-qt-dbus +- Don't require db_cxx.h when compiling with wallet disabled and GUI enabled +- Improve missing boost error reporting +- Upgrade miniupnpc version to 1.9 +- gitian-linux: --enable-glibc-back-compat for binary compatibility with old distributions +- gitian: don't export any symbols from executable +- gitian: build against Qt 4.6 +- devtools: add script to check symbols from Linux gitian executables +- Remove build-time no-IPv6 setting + +GUI: + +- Fix various coin control visual issues +- Show number of in/out connections in debug console +- Show weeks as well as years behind for long timespans behind +- Enable and disable the Show and Remove buttons for requested payments history based on whether any entry is selected. +- Show also value for options overridden on command line in options dialog +- Fill in label from address book also for URIs +- Fixes feel when resizing the last column on tables (issue #2862) +- Fix ESC in disablewallet mode +- Add expert section to wallet tab in optionsdialog +- Do proper boost::path conversion (fixes unicode in datadir) +- Only override -datadir if different from the default (fixes -datadir in config file) +- Show rescan progress at start-up +- Show importwallet progress +- Get required locks upfront in polling functions (avoids hanging on locks) +- Catch Windows shutdown events while client is running +- Optionally add third party links to transaction context menu +- Check for !pixmap() before trying to export QR code (avoids crashes when no QR code could be generated) +- Fix "Start bitcoin on system login" + +Miscellaneous: + +- Replace non-threadsafe C functions (gmtime, strerror and setlocale) +- Add missing cs_main and wallet locks +- Avoid exception at startup when system locale not recognized +- Changed bitrpc.py's raw_input to getpass for passwords to conceal characters during command line input +- devtools: add a script to fetch and postprocess translations + +Credits +-------- + +Thanks to everyone who contributed to this release: + +- Addy Yeow +- Altoidnerd +- Andrea D'Amore +- Andreas Schildbach +- Bardi Harborow +- Brandon Dahler +- Bryan Bishop +- Chris Beams +- Christian von Roques +- Cory Fields +- Cozz Lovan +- daniel +- Daniel Newton +- David A. Harding +- ditto-b +- duanemoody +- Eric S. Bullington +- Fabian Raetz +- Gavin Andresen +- Gregory Maxwell +- gubatron +- Haakon Nilsen +- harry +- Hector Jusforgues +- Isidoro Ghezzi +- Jeff Garzik +- Johnathan Corgan +- jtimon +- Kamil Domanski +- langerhans +- Luke Dashjr +- Manuel Araoz +- Mark Friedenbach +- Matt Corallo +- Matthew Bogosian +- Meeh +- Michael Ford +- Michagogo +- Mikael Wikman +- Mike Hearn +- olalonde +- paveljanik +- peryaudo +- Philip Kaufmann +- philsong +- Pieter Wuille +- R E Broadley +- richierichrawr +- Rune K. Svendsen +- rxl +- shshshsh +- Simon de la Rouviere +- Stuart Cardall +- super3 +- Telepatheic +- Thomas Zander +- Torstein Husebø +- Warren Togami +- Wladimir J. van der Laan +- Yoichi Hirai diff --git a/doc/release-notes/release-notes-0.9.2.md b/doc/release-notes/release-notes-0.9.2.md new file mode 100644 index 0000000000..a2749e549f --- /dev/null +++ b/doc/release-notes/release-notes-0.9.2.md @@ -0,0 +1,207 @@ +Bitcoin Core version 0.9.2 is now available from: + + https://bitcoin.org/bin/0.9.2/ + +This is a new minor version release, bringing mostly bug fixes and some minor +improvements. OpenSSL has been updated because of a security issue (CVE-2014-0224). +Upgrading to this release is recommended. + +Please report bugs using the issue tracker at github: + + https://github.com/bitcoin/bitcoin/issues + +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). + +If you are upgrading from version 0.7.2 or earlier, the first time you run +0.9.2 your blockchain files will be re-indexed, which will take anywhere from +30 minutes to several hours, depending on the speed of your machine. + +Downgrading warnings +-------------------- + +The 'chainstate' for this release is not always compatible with previous +releases, so if you run 0.9.x and then decide to switch back to a +0.8.x release you might get a blockchain validation error when starting the +old release (due to 'pruned outputs' being omitted from the index of +unspent transaction outputs). + +Running the old release with the -reindex option will rebuild the chainstate +data structures and correct the problem. + +Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan +the blockchain for missing spent coins, which will take a long time (tens +of minutes on a typical machine). + +Important changes +================== + +Gitian OSX build +----------------- + +The deterministic build system that was already used for Windows and Linux +builds is now used for OSX as well. Although the resulting executables have +been tested quite a bit, there could be possible regressions. Be sure to report +these on the Github bug tracker mentioned above. + +Compatibility of Linux build +----------------------------- + +For Linux we now build against Qt 4.6, and filter the symbols for libstdc++ and glibc. +This brings back compatibility with + +- Debian 6+ / Tails +- Ubuntu 10.04 +- CentOS 6.5 + +0.9.2 Release notes +======================= + +The OpenSSL dependency in the gitian builds has been upgraded to 1.0.1h because of CVE-2014-0224. + +RPC: + +- Add `getwalletinfo`, `getblockchaininfo` and `getnetworkinfo` calls (will replace hodge-podge `getinfo` at some point) +- Add a `relayfee` field to `getnetworkinfo` +- Fix RPC related shutdown hangs and leaks +- Always show syncnode in `getpeerinfo` +- `sendrawtransaction`: report the reject code and reason, and make it possible to re-send transactions that are already in the mempool +- `getmininginfo` show right genproclimit + +Command-line options: + +- Fix `-printblocktree` output +- Show error message if ReadConfigFile fails + +Block-chain handling and storage: + +- Fix for GetBlockValue() after block 13,440,000 (BIP42) +- Upgrade leveldb to 1.17 + +Protocol and network code: + +- Per-peer block download tracking and stalled download detection +- Add new DNS seed from bitnodes.io +- Prevent socket leak in ThreadSocketHandler and correct some proxy related socket leaks +- Use pnode->nLastRecv as sync score (was the wrong way around) + +Wallet: + +- Make GetAvailableCredit run GetHash() only once per transaction (performance improvement) +- Lower paytxfee warning threshold from 0.25 BTC to 0.01 BTC +- Fix importwallet nTimeFirstKey (trigger necessary rescans) +- Log BerkeleyDB version at startup +- CWallet init fix + +Build system: + +- Add OSX build descriptors to gitian +- Fix explicit --disable-qt-dbus +- Don't require db_cxx.h when compiling with wallet disabled and GUI enabled +- Improve missing boost error reporting +- Upgrade miniupnpc version to 1.9 +- gitian-linux: --enable-glibc-back-compat for binary compatibility with old distributions +- gitian: don't export any symbols from executable +- gitian: build against Qt 4.6 +- devtools: add script to check symbols from Linux gitian executables +- Remove build-time no-IPv6 setting + +GUI: + +- Fix various coin control visual issues +- Show number of in/out connections in debug console +- Show weeks as well as years behind for long timespans behind +- Enable and disable the Show and Remove buttons for requested payments history based on whether any entry is selected. +- Show also value for options overridden on command line in options dialog +- Fill in label from address book also for URIs +- Fixes feel when resizing the last column on tables (issue #2862) +- Fix ESC in disablewallet mode +- Add expert section to wallet tab in optionsdialog +- Do proper boost::path conversion (fixes unicode in datadir) +- Only override -datadir if different from the default (fixes -datadir in config file) +- Show rescan progress at start-up +- Show importwallet progress +- Get required locks upfront in polling functions (avoids hanging on locks) +- Catch Windows shutdown events while client is running +- Optionally add third party links to transaction context menu +- Check for !pixmap() before trying to export QR code (avoids crashes when no QR code could be generated) +- Fix "Start bitcoin on system login" + +Miscellaneous: + +- Replace non-threadsafe C functions (gmtime, strerror and setlocale) +- Add missing cs_main and wallet locks +- Avoid exception at startup when system locale not recognized +- Changed bitrpc.py's raw_input to getpass for passwords to conceal characters during command line input +- devtools: add a script to fetch and postprocess translations + +Credits +-------- + +Thanks to everyone who contributed to this release: + +- Addy Yeow +- Altoidnerd +- Andrea D'Amore +- Andreas Schildbach +- Bardi Harborow +- Brandon Dahler +- Bryan Bishop +- Chris Beams +- Christian von Roques +- Cory Fields +- Cozz Lovan +- daniel +- Daniel Newton +- David A. Harding +- ditto-b +- duanemoody +- Eric S. Bullington +- Fabian Raetz +- Gavin Andresen +- Gregory Maxwell +- gubatron +- Haakon Nilsen +- harry +- Hector Jusforgues +- Isidoro Ghezzi +- Jeff Garzik +- Johnathan Corgan +- jtimon +- Kamil Domanski +- langerhans +- Luke Dashjr +- Manuel Araoz +- Mark Friedenbach +- Matt Corallo +- Matthew Bogosian +- Meeh +- Michael Ford +- Michagogo +- Mikael Wikman +- Mike Hearn +- olalonde +- paveljanik +- peryaudo +- Philip Kaufmann +- philsong +- Pieter Wuille +- R E Broadley +- richierichrawr +- Rune K. Svendsen +- rxl +- shshshsh +- Simon de la Rouviere +- Stuart Cardall +- super3 +- Telepatheic +- Thomas Zander +- Torstein Husebø +- Warren Togami +- Wladimir J. van der Laan +- Yoichi Hirai diff --git a/doc/release-process.md b/doc/release-process.md index 5b96e53753..c588381411 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -23,7 +23,7 @@ Release Process * * * -##perform gitian builds +###perform gitian builds From a directory containing the bitcoin source, gitian-builder and gitian.sigs @@ -33,16 +33,21 @@ Release Process git checkout v${VERSION} popd pushd ./gitian-builder - mkdir -p inputs; cd inputs/ - Register and download the Apple SDK (see OSX Readme for details) - visit https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_4.6.3/xcode4630916281a.dmg +###fetch and build inputs: (first time, or when dependency versions change) - Using a Mac, create a tarball for the 10.7 SDK - tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.7.sdk.tar.gz MacOSX10.7.sdk + mkdir -p inputs; cd inputs/ - Fetch and build inputs: (first time, or when dependency versions change) + Register and download the Apple SDK: (see OSX Readme for details) + + https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_4.6.3/xcode4630916281a.dmg + + Using a Mac, create a tarball for the 10.7 SDK and copy it to the inputs directory: + + tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.7.sdk.tar.gz MacOSX10.7.sdk + Download remaining inputs, and build everything: + wget 'http://miniupnp.free.fr/files/download.php?file=miniupnpc-1.9.tar.gz' -O miniupnpc-1.9.tar.gz wget 'https://www.openssl.org/source/openssl-1.0.1h.tar.gz' wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' @@ -50,9 +55,9 @@ Release Process wget 'ftp://ftp.simplesystems.org/pub/png/src/history/libpng16/libpng-1.6.8.tar.gz' wget 'https://fukuchi.org/works/qrencode/qrencode-3.4.3.tar.bz2' wget 'https://downloads.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.tar.bz2' - wget 'https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch' -O \ - boost-mingw-gas-cross-compile-2013-03-03.patch + wget 'https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch' -O boost-mingw-gas-cross-compile-2013-03-03.patch wget 'https://download.qt-project.org/official_releases/qt/5.2/5.2.0/single/qt-everywhere-opensource-src-5.2.0.tar.gz' + wget 'https://download.qt-project.org/official_releases/qt/5.2/5.2.1/single/qt-everywhere-opensource-src-5.2.1.tar.gz' wget 'https://download.qt-project.org/archive/qt/4.6/qt-everywhere-opensource-src-4.6.4.tar.gz' wget 'https://protobuf.googlecode.com/files/protobuf-2.5.0.tar.bz2' wget 'https://github.com/mingwandroid/toolchain4/archive/10cc648683617cca8bcbeae507888099b41b530c.tar.gz' @@ -61,10 +66,8 @@ Release Process wget 'http://www.opensource.apple.com/tarballs/ld64/ld64-127.2.tar.gz' wget 'http://cdrkit.org/releases/cdrkit-1.1.11.tar.gz' wget 'https://github.com/theuni/libdmg-hfsplus/archive/libdmg-hfsplus-v0.1.tar.gz' - wget 'http://llvm.org/releases/3.2/clang+llvm-3.2-x86-linux-ubuntu-12.04.tar.gz' -O \ - clang-llvm-3.2-x86-linux-ubuntu-12.04.tar.gz - wget 'https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff' -O \ - cdrkit-deterministic.patch + wget 'http://llvm.org/releases/3.2/clang+llvm-3.2-x86-linux-ubuntu-12.04.tar.gz' -O clang-llvm-3.2-x86-linux-ubuntu-12.04.tar.gz + wget 'https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff' -O cdrkit-deterministic.patch cd .. ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/boost-linux.yml mv build/out/boost-*.zip inputs/ @@ -89,10 +92,10 @@ Release Process The expected SHA256 hashes of the intermediate inputs are: - 46710f673467e367738d8806e45b4cb5931aaeea61f4b6b55a68eea56d5006c5 bitcoin-deps-linux32-gitian-r6.zip - f03be39fb26670243d3a659e64d18e19d03dec5c11e9912011107768390b5268 bitcoin-deps-linux64-gitian-r6.zip f29b7d9577417333fb56e023c2977f5726a7c297f320b175a4108cf7cd4c2d29 boost-linux32-1.55.0-gitian-r1.zip 88232451c4104f7eb16e469ac6474fd1231bd485687253f7b2bdf46c0781d535 boost-linux64-1.55.0-gitian-r1.zip + 46710f673467e367738d8806e45b4cb5931aaeea61f4b6b55a68eea56d5006c5 bitcoin-deps-linux32-gitian-r6.zip + f03be39fb26670243d3a659e64d18e19d03dec5c11e9912011107768390b5268 bitcoin-deps-linux64-gitian-r6.zip 57e57dbdadc818cd270e7e00500a5e1085b3bcbdef69a885f0fb7573a8d987e1 qt-linux32-4.6.4-gitian-r1.tar.gz 60eb4b9c5779580b7d66529efa5b2836ba1a70edde2a0f3f696d647906a826be qt-linux64-4.6.4-gitian-r1.tar.gz 60dc2d3b61e9c7d5dbe2f90d5955772ad748a47918ff2d8b74e8db9b1b91c909 boost-win32-1.55.0-gitian-r6.zip @@ -103,11 +106,15 @@ Release Process 751c579830d173ef3e6f194e83d18b92ebef6df03289db13ab77a52b6bc86ef0 qt-win64-5.2.0-gitian-r3.zip e2e403e1a08869c7eed4d4293bce13d51ec6a63592918b90ae215a0eceb44cb4 protobuf-win32-2.5.0-gitian-r4.zip a0999037e8b0ef9ade13efd88fee261ba401f5ca910068b7e0cd3262ba667db0 protobuf-win64-2.5.0-gitian-r4.zip + 512bc0622c883e2e0f4cbc3fedfd8c2402d06c004ce6fb32303cc2a6f405b6df osx-native-depends-r3.tar.gz + 927e4b222be6d590b4bc2fc185872a5d0ca5c322adb983764d3ed84be6bdbc81 osx-depends-r4.tar.gz + ec95abef1df2b096a970359787c01d8c45e2a4475b7ae34e12c022634fbdba8a osx-depends-qt-5.2.1-r4.tar.gz + - Build bitcoind and bitcoin-qt on Linux32, Linux64, and Win32: + Build Bitcoin Core for Linux, Windows, and OS X: ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign --signer $SIGNER --release ${VERSION} --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml pushd build/out zip -r bitcoin-${VERSION}-linux-gitian.zip * mv bitcoin-${VERSION}-linux-gitian.zip ../../../ @@ -130,7 +137,7 @@ Release Process 1. linux 32-bit and 64-bit binaries + source (bitcoin-${VERSION}-linux-gitian.zip) 2. windows 32-bit and 64-bit binaries + installer + source (bitcoin-${VERSION}-win-gitian.zip) 3. OSX installer (Bitcoin-Qt.dmg) - 4. Gitian signatures (in gitian.sigs/${VERSION}[-win|-osx]/(your gitian key)/ + 4. Gitian signatures (in gitian.sigs/${VERSION}-<linux|win|osx>/(your gitian key)/ repackage gitian builds for release as stand-alone zip/tar/installer exe @@ -170,8 +177,9 @@ repackage gitian builds for release as stand-alone zip/tar/installer exe Commit your signature to gitian.sigs: pushd gitian.sigs - git add ${VERSION}/${SIGNER} + git add ${VERSION}-linux/${SIGNER} git add ${VERSION}-win/${SIGNER} + git add ${VERSION}-osx/${SIGNER} git commit -a git push # Assuming you can push to the gitian.sigs tree popd @@ -180,34 +188,6 @@ Commit your signature to gitian.sigs: ### After 3 or more people have gitian-built, repackage gitian-signed zips: -From a directory containing bitcoin source, gitian.sigs and gitian zips - - export VERSION=(new version, e.g. 0.8.0) - mkdir bitcoin-${VERSION}-linux-gitian - pushd bitcoin-${VERSION}-linux-gitian - unzip ../bitcoin-${VERSION}-linux-gitian.zip - mkdir gitian - cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ - for signer in $(ls ../gitian.sigs/${VERSION}/); do - cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert - cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig - done - zip -r bitcoin-${VERSION}-linux-gitian.zip * - cp bitcoin-${VERSION}-linux-gitian.zip ../ - popd - mkdir bitcoin-${VERSION}-win-gitian - pushd bitcoin-${VERSION}-win-gitian - unzip ../bitcoin-${VERSION}-win-gitian.zip - mkdir gitian - cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ - for signer in $(ls ../gitian.sigs/${VERSION}-win/); do - cp ../gitian.sigs/${VERSION}-win/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert - cp ../gitian.sigs/${VERSION}-win/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig - done - zip -r bitcoin-${VERSION}-win-gitian.zip * - cp bitcoin-${VERSION}-win-gitian.zip ../ - popd - - Upload gitian zips to SourceForge - Announce the release: diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py new file mode 100755 index 0000000000..7f2d79b3c1 --- /dev/null +++ b/qa/rpc-tests/receivedby.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Exercise the listtransactions API + +# Add python-bitcoinrpc to module search path: + +import os +import sys +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) + +import json +import shutil +import subprocess +import tempfile +import traceback + +from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from util import * + +def get_sub_array_from_array(object_array, to_match): + ''' + Finds and returns a sub array from an array of arrays. + to_match should be a unique idetifier of a sub array + ''' + num_matched = 0 + for item in object_array: + all_match = True + for key,value in to_match.items(): + if item[key] != value: + all_match = False + if not all_match: + continue + return item + return [] + +def check_array_result(object_array, to_match, expected, should_not_find = False): + """ + Pass in array of JSON objects, a dictionary with key/value pairs + to match against, and another dictionary with expected key/value + pairs. + If the should_not_find flag is true, to_match should not be found in object_array + """ + if should_not_find == True: + expected = { } + num_matched = 0 + for item in object_array: + all_match = True + for key,value in to_match.items(): + if item[key] != value: + all_match = False + if not all_match: + continue + for key,value in expected.items(): + if item[key] != value: + raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value))) + num_matched = num_matched+1 + if num_matched == 0 and should_not_find != True: + raise AssertionError("No objects matched %s"%(str(to_match))) + if num_matched > 0 and should_not_find == True: + raise AssertionError("Objects was matched %s"%(str(to_match))) + +def run_test(nodes): + ''' + listreceivedbyaddress Test + ''' + # Send from node 0 to 1 + addr = nodes[1].getnewaddress() + txid = nodes[0].sendtoaddress(addr, 0.1) + sync_mempools(nodes) + + #Check not listed in listreceivedbyaddress because has 0 confirmations + check_array_result(nodes[1].listreceivedbyaddress(), + {"address":addr}, + { }, + True) + #Bury Tx under 10 block so it will be returned by listreceivedbyaddress + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + check_array_result(nodes[1].listreceivedbyaddress(), + {"address":addr}, + {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) + #With min confidence < 10 + check_array_result(nodes[1].listreceivedbyaddress(5), + {"address":addr}, + {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) + #With min confidence > 10, should not find Tx + check_array_result(nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True) + + #Empty Tx + addr = nodes[1].getnewaddress() + check_array_result(nodes[1].listreceivedbyaddress(0,True), + {"address":addr}, + {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]}) + + ''' + getreceivedbyaddress Test + ''' + # Send from node 0 to 1 + addr = nodes[1].getnewaddress() + txid = nodes[0].sendtoaddress(addr, 0.1) + sync_mempools(nodes) + + #Check balance is 0 because of 0 confirmations + balance = nodes[1].getreceivedbyaddress(addr) + if balance != Decimal("0.0"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + #Check balance is 0.1 + balance = nodes[1].getreceivedbyaddress(addr,0) + if balance != Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + balance = nodes[1].getreceivedbyaddress(addr) + if balance != Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + ''' + listreceivedbyaccount + getreceivedbyaccount Test + ''' + #set pre-state + addrArr = nodes[1].getnewaddress() + account = nodes[1].getaccount(addrArr) + received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(),{"account":account}) + if len(received_by_account_json) == 0: + raise AssertionError("No accounts found in node") + balance_by_account = rec_by_accountArr = nodes[1].getreceivedbyaccount(account) + + txid = nodes[0].sendtoaddress(addr, 0.1) + + # listreceivedbyaccount should return received_by_account_json because of 0 confirmations + check_array_result(nodes[1].listreceivedbyaccount(), + {"account":account}, + received_by_account_json) + + # getreceivedbyaddress should return same balance because of 0 confirmations + balance = nodes[1].getreceivedbyaccount(account) + if balance != balance_by_account: + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + # listreceivedbyaccount should return updated account balance + check_array_result(nodes[1].listreceivedbyaccount(), + {"account":account}, + {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))}) + + # getreceivedbyaddress should return updates balance + balance = nodes[1].getreceivedbyaccount(account) + if balance != balance_by_account + Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + + #Create a new account named "mynewaccount" that has a 0 balance + nodes[1].getaccountaddress("mynewaccount") + received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"}) + if len(received_by_account_json) == 0: + raise AssertionError("No accounts found in node") + + # Test includeempty of listreceivedbyaccount + if received_by_account_json["amount"] != Decimal("0.0"): + raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"])) + + # Test getreceivedbyaccount for 0 amount accounts + balance = nodes[1].getreceivedbyaccount("mynewaccount") + if balance != Decimal("0.0"): + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + +def main(): + import optparse + + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", + help="Leave bitcoinds and test.* datadir on exit or error") + parser.add_option("--srcdir", dest="srcdir", default="../../src", + help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") + parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), + help="Root directory for datadirs") + (options, args) = parser.parse_args() + + os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] + + check_json_precision() + + success = False + nodes = [] + try: + print("Initializing test directory "+options.tmpdir) + if not os.path.isdir(options.tmpdir): + os.makedirs(options.tmpdir) + initialize_chain(options.tmpdir) + + nodes = start_nodes(2, options.tmpdir) + connect_nodes(nodes[1], 0) + sync_blocks(nodes) + + run_test(nodes) + + success = True + + except AssertionError as e: + print("Assertion failed: "+e.message) + except Exception as e: + print("Unexpected exception caught during testing: "+str(e)) + traceback.print_tb(sys.exc_info()[2]) + + if not options.nocleanup: + print("Cleaning up") + stop_nodes(nodes) + wait_bitcoinds() + shutil.rmtree(options.tmpdir) + + if success: + print("Tests successful") + sys.exit(0) + else: + print("Failed") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index a31f8d98ef..a823404e00 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -39,7 +39,7 @@ def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected): stop_nodes(nodes) wait_bitcoinds() -def run_allowip_test(tmpdir, allow_ips, rpchost): +def run_allowip_test(tmpdir, allow_ips, rpchost, rpcport): ''' Start a node with rpcwallow IP, and request getinfo at a non-localhost IP. @@ -48,7 +48,7 @@ def run_allowip_test(tmpdir, allow_ips, rpchost): nodes = start_nodes(1, tmpdir, [base_args]) try: # connect to node through non-loopback interface - url = "http://rt:rt@%s:%d" % (rpchost, START_RPC_PORT,) + url = "http://rt:rt@%s:%d" % (rpchost, rpcport,) node = AuthServiceProxy(url) node.getinfo() finally: @@ -69,15 +69,17 @@ def run_test(tmpdir): assert(not 'This test requires at least one non-loopback IPv4 interface') print("Using interface %s for testing" % non_loopback_ip) + defaultport = rpc_port(0) + # check default without rpcallowip (IPv4 and IPv6 localhost) run_bind_test(tmpdir, None, '127.0.0.1', [], - [('127.0.0.1', 11100), ('::1', 11100)]) + [('127.0.0.1', defaultport), ('::1', defaultport)]) # check default with rpcallowip (IPv6 any) run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', [], - [('::0', 11100)]) + [('::0', defaultport)]) # check only IPv4 localhost (explicit) run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', START_RPC_PORT)]) + [('127.0.0.1', defaultport)]) # check only IPv4 localhost (explicit) with alternative port run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], [('127.0.0.1', 32171)]) @@ -86,18 +88,18 @@ def run_test(tmpdir): [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) # check only IPv6 localhost (explicit) run_bind_test(tmpdir, ['[::1]'], '[::1]', ['[::1]'], - [('::1', 11100)]) + [('::1', defaultport)]) # check both IPv4 and IPv6 localhost (explicit) run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', START_RPC_PORT), ('::1', START_RPC_PORT)]) + [('127.0.0.1', defaultport), ('::1', defaultport)]) # check only non-loopback interface run_bind_test(tmpdir, [non_loopback_ip], non_loopback_ip, [non_loopback_ip], - [(non_loopback_ip, START_RPC_PORT)]) + [(non_loopback_ip, defaultport)]) # Check that with invalid rpcallowip, we are denied - run_allowip_test(tmpdir, [non_loopback_ip], non_loopback_ip) + run_allowip_test(tmpdir, [non_loopback_ip], non_loopback_ip, defaultport) try: - run_allowip_test(tmpdir, ['1.1.1.1'], non_loopback_ip) + run_allowip_test(tmpdir, ['1.1.1.1'], non_loopback_ip, defaultport) assert(not 'Connection not denied by rpcallowip as expected') except ValueError: pass diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py index eded098c7c..27c9f778f6 100644 --- a/qa/rpc-tests/util.py +++ b/qa/rpc-tests/util.py @@ -182,6 +182,10 @@ def wait_bitcoinds(): def connect_nodes(from_connection, node_num): ip_port = "127.0.0.1:"+str(p2p_port(node_num)) from_connection.addnode(ip_port, "onetry") + # poll until version handshake complete to avoid race conditions + # with transaction relaying + while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()): + time.sleep(0.1) def find_output(node, txid, amount): """ diff --git a/qa/rpc-tests/util.sh b/qa/rpc-tests/util.sh index 1e7bd6a7ee..b726ef627f 100644 --- a/qa/rpc-tests/util.sh +++ b/qa/rpc-tests/util.sh @@ -38,6 +38,10 @@ function AssertEqual { if (( $( echo "$1 == $2" | bc ) == 0 )) then echoerr "AssertEqual: $1 != $2" + declare -f CleanUp > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + CleanUp + fi exit 1 fi } @@ -49,6 +53,10 @@ function CheckBalance { if (( $( echo "$B == $EXPECT" | bc ) == 0 )) then echoerr "bad balance: $B (expected $2)" + declare -f CleanUp > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + CleanUp + fi exit 1 fi } diff --git a/qa/rpc-tests/zapwallettxes.sh b/qa/rpc-tests/zapwallettxes.sh new file mode 100755 index 0000000000..bc52a7dacd --- /dev/null +++ b/qa/rpc-tests/zapwallettxes.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Test -zapwallettxes=<mode> + +if [ $# -lt 1 ]; then + echo "Usage: $0 path_to_binaries" + echo "e.g. $0 ../../src" + exit 1 +fi + +set -f + +BITCOIND=${1}/bitcoind +CLI=${1}/bitcoin-cli + +DIR="${BASH_SOURCE%/*}" +SENDANDWAIT="${DIR}/send.sh" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/util.sh" + +D=$(mktemp -d test.XXXXX) + +D1=${D}/node1 +CreateDataDir "$D1" port=11000 rpcport=11001 +B1ARGS="-datadir=$D1" +$BITCOIND $B1ARGS & +B1PID=$! + +D2=${D}/node2 +CreateDataDir "$D2" port=11010 rpcport=11011 +B2ARGS="-datadir=$D2" +$BITCOIND $B2ARGS & +B2PID=$! + +function CleanUp { +$CLI $B2ARGS stop > /dev/null 2>&1 +wait $B2PID +$CLI $B1ARGS stop > /dev/null 2>&1 +wait $B1PID + +rm -rf $D +} + +# 110 blocks, 10 mature == 500 XBT +$CLI $B1ARGS setgenerate true 110 +$CLI $B2ARGS setgenerate true 110 + +CheckBalance "$B1ARGS" 500 +CheckBalance "$B2ARGS" 500 + +# Send 10 XBT +TXID1_DEFAULT=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10) +TXID2_DEFAULT=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10) + +CheckBalance $B1ARGS 490 +CheckBalance $B2ARGS 490 + +# Move 10 XBT to testaccount +TMP=$($CLI $B1ARGS move "" "testaccount" 10) +TMP=$($CLI $B2ARGS move "" "testaccount" 10) + +CheckBalance $B1ARGS 10 "testaccount" +CheckBalance $B2ARGS 10 "testaccount" + +# Send 1 XBT from testaccount +TXID1_TESTACCOUNT=$($CLI $B1ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) +TXID2_TESTACCOUNT=$($CLI $B2ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) + +CheckBalance $B1ARGS 9 "testaccount" +CheckBalance $B2ARGS 9 "testaccount" + +CheckBalance $B1ARGS 489 +CheckBalance $B2ARGS 489 + +# Confirm transactions +$CLI $B1ARGS setgenerate true 1 +$CLI $B2ARGS setgenerate true 1 + +# Create unconfirmed transaction +TXID1_UNCONFIRMED=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) +TXID2_UNCONFIRMED=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) + +# check balance (we created another 50 and spent 1 in the meantime) +CheckBalance $B1ARGS 538 +CheckBalance $B2ARGS 538 + +# Safety check, if unconfirmed transactions are there +$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "gettransaction1_1: $TXID1_UNCONFIRMED failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "gettransaction2_1: $TXID2_UNCONFIRMED failed" + CleanUp + exit 1 +fi + +# stop nodes +$CLI $B2ARGS stop > /dev/null 2>&1 +wait $B2PID +$CLI $B1ARGS stop > /dev/null 2>&1 +wait $B1PID + +# restart nodes with -zapwallettxes +$BITCOIND -zapwallettxes=1 $B1ARGS & +B1PID=$! +$BITCOIND -zapwallettxes=2 $B2ARGS & +B2PID=$! + +# check if confirmed transactions are there +$CLI $B1ARGS gettransaction $TXID1_DEFAULT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 1: $TXID1_DEFAULT failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_DEFAULT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 2: $TXID2_DEFAULT failed" + CleanUp + exit 1 +fi +$CLI $B1ARGS gettransaction $TXID1_TESTACCOUNT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 3: $TXID1_TESTACCOUNT failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_TESTACCOUNT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 4: $TXID2_TESTACCOUNT failed" + CleanUp + exit 1 +fi + +# check if unconfirmed transaction is gone +$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -eq 0 ]] ; then + echoerr "check unconfirmed transaction 1: $TXID1_UNCONFIRMED failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -eq 0 ]] ; then + echoerr "check unconfirmed transaction 2: $TXID2_UNCONFIRMED failed" + CleanUp + exit 1 +fi + +# check zapwallet mode 1, testaccount balance must be 9 (keeping transaction metadata) +CheckBalance $B1ARGS 9 "testaccount" + +# check zapwallet mode 2, testaccount balance must be 10 (dropping transaction metadata) +CheckBalance $B2ARGS 10 "testaccount" + +echo "Tests successful, cleaning up" +CleanUp +exit 0 diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index e6afe3b48b..d4bd585138 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -7,8 +7,9 @@ from subprocess import Popen, PIPE import glob import operator import os +import sys -OUT_CPP="src/qt/bitcoinstrings.cpp" +OUT_CPP="qt/bitcoinstrings.cpp" EMPTY=['""'] def parse_po(text): @@ -47,7 +48,7 @@ def parse_po(text): return messages -files = glob.glob('src/*.cpp') + glob.glob('src/*.h') +files = sys.argv[1:] # xgettext -n --keyword=_ $FILES XGETTEXT=os.getenv('XGETTEXT', 'xgettext') diff --git a/src/Makefile.am b/src/Makefile.am index 3016be47b9..9b0b97b7ac 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,10 @@ AM_CPPFLAGS = $(INCLUDES) -AM_LDFLAGS = $(PTHREAD_CFLAGS) +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) +if USE_LIBSECP256K1 +secp256k1/libsecp256k1.la: $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) + @$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +endif if EMBEDDED_LEVELDB LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include @@ -17,12 +21,27 @@ $(LIBLEVELDB) $(LIBMEMENV): OPT="$(CXXFLAGS) $(CPPFLAGS)" endif +BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) +if USE_LIBSECP256K1 +BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include +endif + +LIBBITCOIN_SERVER=libbitcoin_server.a +LIBBITCOIN_WALLET=libbitcoin_wallet.a +LIBBITCOIN_COMMON=libbitcoin_common.a +LIBBITCOIN_CLI=libbitcoin_cli.a +LIBBITCOIN_UTIL=libbitcoin_util.a +LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a +LIBBITCOINQT=qt/libbitcoinqt.a + noinst_LIBRARIES = \ libbitcoin_server.a \ libbitcoin_common.a \ - libbitcoin_cli.a + libbitcoin_cli.a \ + libbitcoin_util.a \ + crypto/libbitcoin_crypto.a if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) noinst_LIBRARIES += libbitcoin_wallet.a @@ -48,6 +67,7 @@ BITCOIN_CORE_H = \ base58.h \ bloom.h \ chainparams.h \ + chainparamsbase.h \ checkpoints.h \ checkqueue.h \ clientversion.h \ @@ -69,6 +89,7 @@ BITCOIN_CORE_H = \ netbase.h \ net.h \ noui.h \ + pow.h \ protocol.h \ rpcclient.h \ rpcprotocol.h \ @@ -77,6 +98,7 @@ BITCOIN_CORE_H = \ serialize.h \ sync.h \ threadsafety.h \ + timedata.h \ tinyformat.h \ txdb.h \ txmempool.h \ @@ -85,7 +107,8 @@ BITCOIN_CORE_H = \ util.h \ version.h \ walletdb.h \ - wallet.h + wallet.h \ + compat/sanity.h JSON_H = \ json/json_spirit.h \ @@ -102,33 +125,36 @@ obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ $(abs_top_srcdir) -libbitcoin_common_a-version.$(OBJEXT): obj/build.h +libbitcoin_util_a-version.$(OBJEXT): obj/build.h +# server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_server_a_SOURCES = \ addrman.cpp \ alert.cpp \ bloom.cpp \ checkpoints.cpp \ - coins.cpp \ init.cpp \ - keystore.cpp \ leveldbwrapper.cpp \ main.cpp \ miner.cpp \ net.cpp \ noui.cpp \ + pow.cpp \ rpcblockchain.cpp \ rpcmining.cpp \ rpcmisc.cpp \ rpcnet.cpp \ rpcrawtransaction.cpp \ rpcserver.cpp \ + timedata.cpp \ txdb.cpp \ txmempool.cpp \ $(JSON_H) \ $(BITCOIN_CORE_H) +# wallet: shared between bitcoind and bitcoin-qt, but only linked +# when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_wallet_a_SOURCES = \ db.cpp \ @@ -139,42 +165,74 @@ libbitcoin_wallet_a_SOURCES = \ walletdb.cpp \ $(BITCOIN_CORE_H) +# crypto primitives library +crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) +crypto_libbitcoin_crypto_a_SOURCES = \ + crypto/sha1.cpp \ + crypto/sha2.cpp \ + crypto/ripemd160.cpp \ + crypto/common.h \ + crypto/sha2.h \ + crypto/sha1.h \ + crypto/ripemd160.h + +# common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_common_a_SOURCES = \ - base58.cpp \ allocators.cpp \ + base58.cpp \ chainparams.cpp \ + coins.cpp \ core.cpp \ hash.cpp \ key.cpp \ + keystore.cpp \ netbase.cpp \ protocol.cpp \ - rpcprotocol.cpp \ script.cpp \ + $(BITCOIN_CORE_H) + +# util: shared between all executables. +# This library *must* be included to make sure that the glibc +# backward-compatibility objects and their sanity checks are linked. +libbitcoin_util_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_util_a_SOURCES = \ + chainparamsbase.cpp \ + rpcprotocol.cpp \ sync.cpp \ + uint256.cpp \ util.cpp \ version.cpp \ + compat/glibc_sanity.cpp \ + compat/glibcxx_sanity.cpp \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT -libbitcoin_common_a_SOURCES += compat/glibc_compat.cpp -libbitcoin_common_a_SOURCES += compat/glibcxx_compat.cpp +libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp +libbitcoin_util_a_SOURCES += compat/glibcxx_compat.cpp endif +# cli: shared between bitcoin-cli and bitcoin-qt libbitcoin_cli_a_SOURCES = \ rpcclient.cpp \ $(BITCOIN_CORE_H) -nodist_libbitcoin_common_a_SOURCES = $(srcdir)/obj/build.h +nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # # bitcoind binary # bitcoind_LDADD = \ - libbitcoin_server.a \ - libbitcoin_cli.a \ - libbitcoin_common.a \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBMEMENV) + +if USE_LIBSECP256K1 + bitcoind_LDADD += secp256k1/libsecp256k1.la +endif + if ENABLE_WALLET bitcoind_LDADD += libbitcoin_wallet.a endif @@ -190,10 +248,17 @@ bitcoind_CPPFLAGS = $(BITCOIN_INCLUDES) # bitcoin-cli binary # bitcoin_cli_LDADD = \ - libbitcoin_cli.a \ - libbitcoin_common.a \ + $(LIBBITCOIN_CLI) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) \ $(BOOST_LIBS) -bitcoin_cli_SOURCES = bitcoin-cli.cpp +bitcoin_cli_SOURCES = \ + bitcoin-cli.cpp + +if USE_LIBSECP256K1 + bitcoin_cli_LDADD += secp256k1/libsecp256k1.la +endif bitcoin_cli_CPPFLAGS = $(BITCOIN_INCLUDES) # @@ -205,11 +270,13 @@ CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno DISTCLEANFILES = obj/build.h -EXTRA_DIST = leveldb +EXTRA_DIST = leveldb secp256k1 clean-local: -$(MAKE) -C leveldb clean + -$(MAKE) -C secp256k1 clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno + -rm -f config.h .rc.o: @test -f $(WINDRES) @@ -223,12 +290,6 @@ clean-local: @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(<D) $<) -LIBBITCOIN_SERVER=libbitcoin_server.a -LIBBITCOIN_WALLET=libbitcoin_wallet.a -LIBBITCOIN_COMMON=libbitcoin_common.a -LIBBITCOIN_CLI=libbitcoin_cli.a -LIBBITCOINQT=qt/libbitcoinqt.a - if ENABLE_TESTS include Makefile.test.include endif diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 647434e1ef..75b7b683dd 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -252,7 +252,10 @@ RES_ICONS = \ qt/res/icons/tx_inout.png \ qt/res/icons/tx_input.png \ qt/res/icons/tx_output.png \ - qt/res/icons/tx_mined.png + qt/res/icons/tx_mined.png \ + qt/res/icons/unit_btc.png \ + qt/res/icons/unit_mbtc.png \ + qt/res/icons/unit_ubtc.png BITCOIN_QT_CPP = \ qt/bitcoinaddressvalidator.cpp \ @@ -355,18 +358,21 @@ qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER) if ENABLE_WALLET qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) endif -qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ +qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) -qt_bitcoin_qt_LDFLAGS = $(QT_LDFLAGS) +if USE_LIBSECP256K1 + qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la +endif +qt_bitcoin_qt_LDFLAGS = $(AM_LDFLAGS) $(QT_LDFLAGS) #locale/foo.ts -> locale/foo.qm QT_QM=$(QT_TS:.ts=.qm) .SECONDARY: $(QT_QM) -qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_cli_a_SOURCES) +qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" - $(AM_V_GEN) cd $(top_srcdir); XGETTEXT=$(XGETTEXT) share/qt/extract_strings_qt.py + $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) ../share/qt/extract_strings_qt.py $^ translate: qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index e0b49d0240..51ce006fc1 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -30,10 +30,13 @@ qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) if ENABLE_WALLET qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) endif -qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ +qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) -qt_test_test_bitcoin_qt_LDFLAGS = $(QT_LDFLAGS) +if USE_LIBSECP256K1 + qt_test_test_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la +endif +qt_test_test_bitcoin_qt_LDFLAGS = $(AM_LDFLAGS) $(QT_LDFLAGS) CLEAN_BITCOIN_QT_TEST = $(TEST_QT_MOC_CPP) qt/test/*.gcda qt/test/*.gcno diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 988830260c..12b90adca3 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,8 +31,10 @@ BITCOIN_TESTS =\ test/checkblock_tests.cpp \ test/Checkpoints_tests.cpp \ test/compress_tests.cpp \ + test/crypto_tests.cpp \ test/DoS_tests.cpp \ test/getarg_tests.cpp \ + test/hash_tests.cpp \ test/key_tests.cpp \ test/main_tests.cpp \ test/miner_tests.cpp \ @@ -45,6 +47,7 @@ BITCOIN_TESTS =\ test/script_tests.cpp \ test/serialize_tests.cpp \ test/sigopcount_tests.cpp \ + test/skiplist_tests.cpp \ test/test_bitcoin.cpp \ test/transaction_tests.cpp \ test/uint256_tests.cpp \ @@ -61,11 +64,16 @@ endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) -test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ +test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif + +if USE_LIBSECP256K1 + test_test_bitcoin_LDADD += secp256k1/libsecp256k1.la +endif + test_test_bitcoin_LDADD += $(BDB_LIBS) nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/addrman.h b/src/addrman.h index 5328a93b45..c4c296560e 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -8,6 +8,7 @@ #include "netbase.h" #include "protocol.h" #include "sync.h" +#include "timedata.h" #include "util.h" #include <map> diff --git a/src/alert.cpp b/src/alert.cpp index 638f0d7a1c..258a2b52c4 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -8,6 +8,7 @@ #include "chainparams.h" #include "key.h" #include "net.h" +#include "timedata.h" #include "ui_interface.h" #include "util.h" diff --git a/src/base58.cpp b/src/base58.cpp index 5975703887..c9e91beef1 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -114,9 +114,8 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) { } bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) { - if (!DecodeBase58(psz, vchRet)) - return false; - if (vchRet.size() < 4) + if (!DecodeBase58(psz, vchRet) || + (vchRet.size() < 4)) { vchRet.clear(); return false; @@ -154,8 +153,8 @@ void CBase58Data::SetData(const std::vector<unsigned char> &vchVersionIn, const bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) { std::vector<unsigned char> vchTemp; - DecodeBase58Check(psz, vchTemp); - if (vchTemp.size() < nVersionBytes) { + bool rc58 = DecodeBase58Check(psz, vchTemp); + if ((!rc58) || (vchTemp.size() < nVersionBytes)) { vchData.clear(); vchVersion.clear(); return false; @@ -187,6 +186,7 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const { } namespace { + class CBitcoinAddressVisitor : public boost::static_visitor<bool> { private: CBitcoinAddress *addr; @@ -197,7 +197,8 @@ namespace { bool operator()(const CScriptID &id) const { return addr->Set(id); } bool operator()(const CNoDestination &no) const { return false; } }; -}; + +} // anon namespace bool CBitcoinAddress::Set(const CKeyID &id) { SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); diff --git a/src/bitcoin-cli-res.rc b/src/bitcoin-cli-res.rc index f8bfb3a881..b1aa1b0e16 100644 --- a/src/bitcoin-cli-res.rc +++ b/src/bitcoin-cli-res.rc @@ -5,7 +5,6 @@ #define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) #define VER_FILEVERSION VER_PRODUCTVERSION #define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR -#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin Core developers" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 0bb71329f5..016b2f50f5 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -7,11 +7,39 @@ #include "init.h" #include "rpcclient.h" #include "rpcprotocol.h" -#include "ui_interface.h" /* for _(...) */ -#include "chainparams.h" +#include "chainparamsbase.h" #include <boost/filesystem/operations.hpp> +#define _(x) std::string(x) /* Keep the _() around in case gettext or such will be used later to translate non-UI */ + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +std::string HelpMessageCli() +{ + string strUsage; + strUsage += _("Options:") + "\n"; + strUsage += " -? " + _("This help message") + "\n"; + strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; + strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; + strUsage += " -testnet " + _("Use the test network") + "\n"; + strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " + "solved instantly. This is intended for regression testing tools and app development.") + "\n"; + strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; + strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; + strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; + strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; + strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; + + strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; + strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; + + return strUsage; +} + ////////////////////////////////////////////////////////////////////////////// // // Start @@ -33,12 +61,11 @@ static bool AppInitRPC(int argc, char* argv[]) fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return false; } - // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) - if (!SelectParamsFromCommandLine()) { + // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) + if (!SelectBaseParamsFromCommandLine()) { fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); return false; } - if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) { std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n"; @@ -49,7 +76,7 @@ static bool AppInitRPC(int argc, char* argv[]) " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessageCli(true); + strUsage += "\n" + HelpMessageCli(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -58,6 +85,136 @@ static bool AppInitRPC(int argc, char* argv[]) return true; } +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword=<password> in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().string().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl", false); + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); + SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); + + bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started + do { + bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort()))); + if (fConnected) break; + if (fWait) + MilliSleep(1000); + else + throw runtime_error("couldn't connect to server"); + } while (fWait); + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map<string, string> mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body + map<string, string> mapHeaders; + string strReply; + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + + if (nStatus == HTTP_UNAUTHORIZED) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + std::vector<std::string> strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + strPrint = string("error: ") + e.what(); + nRet = EXIT_FAILURE; + } + catch (...) { + PrintExceptionContinue(NULL, "CommandLineRPC()"); + throw; + } + + if (strPrint != "") + { + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + } + return nRet; +} + int main(int argc, char* argv[]) { SetupEnvironment(); @@ -65,17 +222,17 @@ int main(int argc, char* argv[]) try { if(!AppInitRPC(argc, argv)) - return abs(RPC_MISC_ERROR); + return EXIT_FAILURE; } catch (std::exception& e) { PrintExceptionContinue(&e, "AppInitRPC()"); - return abs(RPC_MISC_ERROR); + return EXIT_FAILURE; } catch (...) { PrintExceptionContinue(NULL, "AppInitRPC()"); - return abs(RPC_MISC_ERROR); + return EXIT_FAILURE; } - int ret = abs(RPC_MISC_ERROR); + int ret = EXIT_FAILURE; try { ret = CommandLineRPC(argc, argv); diff --git a/src/bitcoind-res.rc b/src/bitcoind-res.rc index dc5c56b797..2e6d754495 100644 --- a/src/bitcoind-res.rc +++ b/src/bitcoind-res.rc @@ -5,7 +5,6 @@ #define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) #define VER_FILEVERSION VER_PRODUCTVERSION #define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR -#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin Core developers" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b7d8ee7f90..880955481b 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -4,7 +4,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcserver.h" -#include "rpcclient.h" #include "init.h" #include "main.h" #include "noui.h" @@ -94,14 +93,9 @@ bool AppInit(int argc, char* argv[]) else { strUsage += "\n" + _("Usage:") + "\n" + - " bitcoind [options] " + _("Start Bitcoin Core Daemon") + "\n" + - _("Usage (deprecated, use bitcoin-cli):") + "\n" + - " bitcoind [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" + - " bitcoind [options] help " + _("List commands") + "\n" + - " bitcoind [options] help <command> " + _("Get help for a command") + "\n"; + " bitcoind [options] " + _("Start Bitcoin Core Daemon") + "\n"; strUsage += "\n" + HelpMessage(HMM_BITCOIND); - strUsage += "\n" + HelpMessageCli(false); } fprintf(stdout, "%s", strUsage.c_str()); @@ -116,8 +110,8 @@ bool AppInit(int argc, char* argv[]) if (fCommandLine) { - int ret = CommandLineRPC(argc, argv); - exit(ret); + fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n"); + exit(1); } #ifndef WIN32 fDaemon = GetBoolArg("-daemon", false); diff --git a/src/bloom.cpp b/src/bloom.cpp index 1bfcbd406f..85a2ddc189 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -94,12 +94,19 @@ bool CBloomFilter::contains(const uint256& hash) const return contains(data); } +void CBloomFilter::clear() +{ + vData.assign(vData.size(),0); + isFull = false; + isEmpty = true; +} + bool CBloomFilter::IsWithinSizeConstraints() const { return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; } -bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) +bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) { bool fFound = false; // Match if the filter contains the hash of tx @@ -108,6 +115,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha return true; if (isEmpty) return false; + const uint256& hash = tx.GetHash(); if (contains(hash)) fFound = true; diff --git a/src/bloom.h b/src/bloom.h index 75e3f38c55..d0caf9e9fa 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -78,12 +78,14 @@ public: bool contains(const COutPoint& outpoint) const; bool contains(const uint256& hash) const; + void clear(); + // 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) bool IsWithinSizeConstraints() const; // Also adds any outputs which match the filter to the filter (to match their spending txes) - bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); + bool IsRelevantAndUpdate(const CTransaction& tx); // Checks for empty and full filters to avoid wasting cpu void UpdateEmptyFull(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 18d6a63603..b9097ea961 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -10,6 +10,7 @@ #include <boost/assign/list_of.hpp> +using namespace std; using namespace boost::assign; // @@ -98,7 +99,8 @@ unsigned int pnSeed[] = class CMainParams : public CChainParams { public: CMainParams() { - networkID = CChainParams::MAIN; + networkID = CBaseChainParams::MAIN; + strNetworkID = "main"; // The message start string is designed to be unlikely to occur in normal data. // The characters are rarely used upper ASCII, not valid as UTF-8, and produce // a large 4-byte int at any alignment. @@ -108,13 +110,14 @@ public: pchMessageStart[3] = 0xd9; vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"); nDefaultPort = 8333; - nRPCPort = 8332; bnProofOfWorkLimit = ~uint256(0) >> 32; nSubsidyHalvingInterval = 210000; nEnforceBlockUpgradeMajority = 750; nRejectBlockOutdatedMajority = 950; nToCheckBlockUpgradeMajority = 1000; nMinerThreads = 0; + nTargetTimespan = 14 * 24 * 60 * 60; // two weeks + nTargetSpacing = 10 * 60; // Build the genesis block. Note that the output of the genesis coinbase cannot // be spent as it did not originally exist in the database. @@ -125,7 +128,7 @@ public: // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) // vMerkleTree: 4a5e1e const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - CTransaction txNew; + CMutableTransaction txNew; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); @@ -177,7 +180,6 @@ public: fDefaultCheckMemPool = false; fAllowMinDifficultyBlocks = false; fRequireStandard = true; - fRPCisTestNet = false; fMineBlocksOnDemand = false; } }; @@ -189,7 +191,8 @@ static CMainParams mainParams; class CTestNetParams : public CMainParams { public: CTestNetParams() { - networkID = CChainParams::TESTNET; + networkID = CBaseChainParams::TESTNET; + strNetworkID = "test"; // The message start string is designed to be unlikely to occur in normal data. // The characters are rarely used upper ASCII, not valid as UTF-8, and produce // a large 4-byte int at any alignment. @@ -199,11 +202,12 @@ public: pchMessageStart[3] = 0x07; vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"); nDefaultPort = 18333; - nRPCPort = 18332; nEnforceBlockUpgradeMajority = 51; nRejectBlockOutdatedMajority = 75; nToCheckBlockUpgradeMajority = 100; - strDataDir = "testnet3"; + nMinerThreads = 0; + nTargetTimespan = 14 * 24 * 60 * 60; // two weeks + nTargetSpacing = 10 * 60; // Modify the testnet genesis block so the timestamp is valid for a later start. genesis.nTime = 1296688602; @@ -228,7 +232,6 @@ public: fDefaultCheckMemPool = false; fAllowMinDifficultyBlocks = true; fRequireStandard = false; - fRPCisTestNet = true; fMineBlocksOnDemand = false; } }; @@ -240,7 +243,8 @@ static CTestNetParams testNetParams; class CRegTestParams : public CTestNetParams { public: CRegTestParams() { - networkID = CChainParams::REGTEST; + networkID = CBaseChainParams::REGTEST; + strNetworkID = "regtest"; pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; pchMessageStart[2] = 0xb5; @@ -250,13 +254,14 @@ public: nRejectBlockOutdatedMajority = 950; nToCheckBlockUpgradeMajority = 1000; nMinerThreads = 1; + nTargetTimespan = 14 * 24 * 60 * 60; // two weeks + nTargetSpacing = 10 * 60; bnProofOfWorkLimit = ~uint256(0) >> 1; genesis.nTime = 1296688602; genesis.nBits = 0x207fffff; genesis.nNonce = 2; hashGenesisBlock = genesis.GetHash(); nDefaultPort = 18444; - strDataDir = "regtest"; assert(hashGenesisBlock == uint256("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); vSeeds.clear(); // Regtest mode doesn't have any DNS seeds. @@ -266,27 +271,28 @@ public: fDefaultCheckMemPool = true; fAllowMinDifficultyBlocks = true; fRequireStandard = false; - fRPCisTestNet = true; fMineBlocksOnDemand = true; } }; static CRegTestParams regTestParams; -static CChainParams *pCurrentParams = &mainParams; +static CChainParams *pCurrentParams = 0; const CChainParams &Params() { + assert(pCurrentParams); return *pCurrentParams; } -void SelectParams(CChainParams::Network network) { +void SelectParams(CBaseChainParams::Network network) { + SelectBaseParams(network); switch (network) { - case CChainParams::MAIN: + case CBaseChainParams::MAIN: pCurrentParams = &mainParams; break; - case CChainParams::TESTNET: + case CBaseChainParams::TESTNET: pCurrentParams = &testNetParams; break; - case CChainParams::REGTEST: + case CBaseChainParams::REGTEST: pCurrentParams = ®TestParams; break; default: @@ -296,19 +302,9 @@ void SelectParams(CChainParams::Network network) { } bool SelectParamsFromCommandLine() { - bool fRegTest = GetBoolArg("-regtest", false); - bool fTestNet = GetBoolArg("-testnet", false); - - if (fTestNet && fRegTest) { + if (!SelectBaseParamsFromCommandLine()) return false; - } - if (fRegTest) { - SelectParams(CChainParams::REGTEST); - } else if (fTestNet) { - SelectParams(CChainParams::TESTNET); - } else { - SelectParams(CChainParams::MAIN); - } + SelectParams(BaseParams().NetworkID()); return true; } diff --git a/src/chainparams.h b/src/chainparams.h index 8370cc5690..446256ba82 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -7,18 +7,17 @@ #define BITCOIN_CHAIN_PARAMS_H #include "core.h" +#include "chainparamsbase.h" #include "protocol.h" #include "uint256.h" #include <vector> -using namespace std; - typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; struct CDNSSeedData { - string name, host; - CDNSSeedData(const string &strName, const string &strHost) : name(strName), host(strHost) {} + std::string name, host; + CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {} }; /** @@ -31,14 +30,6 @@ struct CDNSSeedData { class CChainParams { public: - enum Network { - MAIN, - TESTNET, - REGTEST, - - MAX_NETWORK_TYPES - }; - enum Base58Type { PUBKEY_ADDRESS, SCRIPT_ADDRESS, @@ -51,7 +42,7 @@ public: const uint256& HashGenesisBlock() const { return hashGenesisBlock; } const MessageStartChars& MessageStart() const { return pchMessageStart; } - const vector<unsigned char>& AlertKey() const { return vAlertPubKey; } + const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } const uint256& ProofOfWorkLimit() const { return bnProofOfWorkLimit; } int SubsidyHalvingInterval() const { return nSubsidyHalvingInterval; } @@ -72,44 +63,45 @@ public: bool AllowMinDifficultyBlocks() const { return fAllowMinDifficultyBlocks; } /* Make standard checks */ bool RequireStandard() const { return fRequireStandard; } - /* Make standard checks */ - bool RPCisTestNet() const { return fRPCisTestNet; } - const string& DataDir() const { return strDataDir; } + int64_t TargetTimespan() const { return nTargetTimespan; } + int64_t TargetSpacing() const { return nTargetSpacing; } + int64_t Interval() const { return nTargetTimespan / nTargetSpacing; } /* Make miner stop after a block is found. In RPC, don't return * until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } - Network NetworkID() const { return networkID; } - const vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } + CBaseChainParams::Network NetworkID() const { return networkID; } + /* Return the BIP70 network string (main, test or regtest) */ + std::string NetworkIDString() const { return strNetworkID; } + const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } - const vector<CAddress>& FixedSeeds() const { return vFixedSeeds; } - int RPCPort() const { return nRPCPort; } + const std::vector<CAddress>& FixedSeeds() const { return vFixedSeeds; } protected: CChainParams() {} uint256 hashGenesisBlock; MessageStartChars pchMessageStart; // Raw pub key bytes for the broadcast alert signing key. - vector<unsigned char> vAlertPubKey; + std::vector<unsigned char> vAlertPubKey; int nDefaultPort; - int nRPCPort; uint256 bnProofOfWorkLimit; int nSubsidyHalvingInterval; int nEnforceBlockUpgradeMajority; int nRejectBlockOutdatedMajority; int nToCheckBlockUpgradeMajority; - string strDataDir; + int64_t nTargetTimespan; + int64_t nTargetSpacing; int nMinerThreads; - vector<CDNSSeedData> vSeeds; + std::vector<CDNSSeedData> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; - Network networkID; + CBaseChainParams::Network networkID; + std::string strNetworkID; CBlock genesis; - vector<CAddress> vFixedSeeds; + std::vector<CAddress> vFixedSeeds; bool fRequireRPCPassword; bool fMiningRequiresPeers; bool fDefaultCheckMemPool; bool fAllowMinDifficultyBlocks; bool fRequireStandard; - bool fRPCisTestNet; bool fMineBlocksOnDemand; }; @@ -120,7 +112,7 @@ protected: const CChainParams &Params(); /** Sets the params returned by Params() to those for the given network. */ -void SelectParams(CChainParams::Network network); +void SelectParams(CBaseChainParams::Network network); /** * Looks for -regtest or -testnet and then calls SelectParams as appropriate. diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp new file mode 100644 index 0000000000..19a9e72cc9 --- /dev/null +++ b/src/chainparamsbase.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparamsbase.h" + +#include "assert.h" +#include "util.h" + +#include <boost/assign/list_of.hpp> + +using namespace boost::assign; + +// +// Main network +// + +class CBaseMainParams : public CBaseChainParams { +public: + CBaseMainParams() { + networkID = CBaseChainParams::MAIN; + nRPCPort = 8332; + } +}; +static CBaseMainParams mainParams; + +// +// Testnet (v3) +// +class CBaseTestNetParams : public CBaseMainParams { +public: + CBaseTestNetParams() { + networkID = CBaseChainParams::TESTNET; + nRPCPort = 18332; + strDataDir = "testnet3"; + } +}; +static CBaseTestNetParams testNetParams; + +// +// Regression test +// +class CBaseRegTestParams : public CBaseTestNetParams { +public: + CBaseRegTestParams() { + networkID = CBaseChainParams::REGTEST; + strDataDir = "regtest"; + } +}; +static CBaseRegTestParams regTestParams; + +static CBaseChainParams *pCurrentBaseParams = 0; + +const CBaseChainParams &BaseParams() { + assert(pCurrentBaseParams); + return *pCurrentBaseParams; +} + +void SelectBaseParams(CBaseChainParams::Network network) { + switch (network) { + case CBaseChainParams::MAIN: + pCurrentBaseParams = &mainParams; + break; + case CBaseChainParams::TESTNET: + pCurrentBaseParams = &testNetParams; + break; + case CBaseChainParams::REGTEST: + pCurrentBaseParams = ®TestParams; + break; + default: + assert(false && "Unimplemented network"); + return; + } +} + +bool SelectBaseParamsFromCommandLine() { + bool fRegTest = GetBoolArg("-regtest", false); + bool fTestNet = GetBoolArg("-testnet", false); + + if (fTestNet && fRegTest) { + return false; + } + + if (fRegTest) { + SelectBaseParams(CBaseChainParams::REGTEST); + } else if (fTestNet) { + SelectBaseParams(CBaseChainParams::TESTNET); + } else { + SelectBaseParams(CBaseChainParams::MAIN); + } + return true; +} diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h new file mode 100644 index 0000000000..4a3b268909 --- /dev/null +++ b/src/chainparamsbase.h @@ -0,0 +1,52 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CHAIN_PARAMS_BASE_H +#define BITCOIN_CHAIN_PARAMS_BASE_H + +#include <vector> +#include <string> + +/** + * CBaseChainParams defines the base parameters (shared between bitcoin-cli and bitcoind) + * of a given instance of the Bitcoin system. + */ +class CBaseChainParams +{ +public: + enum Network { + MAIN, + TESTNET, + REGTEST, + + MAX_NETWORK_TYPES + }; + + const std::string& DataDir() const { return strDataDir; } + int RPCPort() const { return nRPCPort; } + Network NetworkID() const { return networkID; } +protected: + CBaseChainParams() {} + + int nRPCPort; + std::string strDataDir; + Network networkID; +}; + +/** + * Return the currently selected parameters. This won't change after app startup + * outside of the unit tests. + */ +const CBaseChainParams &BaseParams(); + +/** Sets the params returned by Params() to those for the given network. */ +void SelectBaseParams(CBaseChainParams::Network network); + +/** + * Looks for -regtest or -testnet and then calls SelectParams as appropriate. + * Returns false if an invalid combination is given. + */ +bool SelectBaseParamsFromCommandLine(); + +#endif diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 926949e06a..4cab11db3d 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -12,8 +12,8 @@ #include <boost/assign/list_of.hpp> // for 'map_list_of()' #include <boost/foreach.hpp> -namespace Checkpoints -{ +namespace Checkpoints { + typedef std::map<int, uint256> MapCheckpoints; // How many times we expect transactions after the last checkpoint to @@ -83,9 +83,9 @@ namespace Checkpoints }; const CCheckpointData &Checkpoints() { - if (Params().NetworkID() == CChainParams::TESTNET) + if (Params().NetworkID() == CBaseChainParams::TESTNET) return dataTestnet; - else if (Params().NetworkID() == CChainParams::MAIN) + else if (Params().NetworkID() == CBaseChainParams::MAIN) return data; else return dataRegtest; @@ -127,7 +127,7 @@ namespace Checkpoints } else { double nCheapBefore = data.nTransactionsLastCheckpoint; double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; - double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; + double nExpensiveAfter = (nNow - pindex->GetBlockTime())/86400.0*data.fTransactionsPerDay; fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; } @@ -161,4 +161,5 @@ namespace Checkpoints } return NULL; } -} + +} // namespace Checkpoints diff --git a/src/checkpoints.h b/src/checkpoints.h index 1b4aacee20..2cf8d41b9d 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -13,8 +13,8 @@ class uint256; /** Block-chain checkpoints are compiled-in sanity checks. * They are updated every release or three. */ -namespace Checkpoints -{ +namespace Checkpoints { + // Returns true if block passes checkpoint checks bool CheckBlock(int nHeight, const uint256& hash); @@ -27,6 +27,7 @@ namespace Checkpoints double GuessVerificationProgress(CBlockIndex *pindex, bool fSigchecks = true); extern bool fEnabled; -} + +} //namespace Checkpoints #endif diff --git a/src/clientversion.h b/src/clientversion.h index 29b4aa3764..6c718a9f79 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -2,13 +2,13 @@ #define CLIENTVERSION_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #else // // client versioning and copyright year // -// These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it +// These need to be macros, as version.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 9 #define CLIENT_VERSION_REVISION 99 @@ -28,4 +28,7 @@ #define STRINGIZE(X) DO_STRINGIZE(X) #define DO_STRINGIZE(X) #X +// Copyright string used in Windows .rc files +#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin Core Developers" + #endif // CLIENTVERSION_H diff --git a/src/coins.cpp b/src/coins.cpp index 86b2a6ef17..13a4ea95cd 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -55,7 +55,7 @@ bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return fal bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } uint256 CCoinsView::GetBestBlock() { return uint256(0); } bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } -bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return false; } +bool CCoinsView::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } @@ -66,7 +66,7 @@ bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(t uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } -bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } +bool CCoinsViewBacked::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } @@ -83,20 +83,20 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { return false; } -std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); +CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + CCoinsMap::iterator it = cacheCoins.lower_bound(txid); if (it != cacheCoins.end() && it->first == txid) return it; CCoins tmp; if (!base->GetCoins(txid,tmp)) return cacheCoins.end(); - std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + CCoinsMap::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); tmp.swap(ret->second); return ret; } CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = FetchCoins(txid); + CCoinsMap::iterator it = FetchCoins(txid); assert(it != cacheCoins.end()); return it->second; } @@ -121,8 +121,8 @@ bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { return true; } -bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlockIn) { - for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) +bool CCoinsViewCache::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlockIn) { + for (CCoinsMap::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) cacheCoins[it->first] = it->second; hashBlock = hashBlockIn; return true; diff --git a/src/coins.h b/src/coins.h index 0ad28524a1..c57a5ec722 100644 --- a/src/coins.h +++ b/src/coins.h @@ -239,6 +239,7 @@ public: } }; +typedef std::map<uint256,CCoins> CCoinsMap; struct CCoinsStats { @@ -275,7 +276,7 @@ public: virtual bool SetBestBlock(const uint256 &hashBlock); // Do a bulk modification (multiple SetCoins + one SetBestBlock) - virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + virtual bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock); // Calculate statistics about the unspent transaction output set virtual bool GetStats(CCoinsStats &stats); @@ -299,7 +300,7 @@ public: uint256 GetBestBlock(); bool SetBestBlock(const uint256 &hashBlock); void SetBackend(CCoinsView &viewIn); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; @@ -309,7 +310,7 @@ class CCoinsViewCache : public CCoinsViewBacked { protected: uint256 hashBlock; - std::map<uint256,CCoins> cacheCoins; + CCoinsMap cacheCoins; public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); @@ -320,7 +321,7 @@ public: bool HaveCoins(const uint256 &txid); uint256 GetBestBlock(); bool SetBestBlock(const uint256 &hashBlock); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock); // Return a modifiable reference to a CCoins. Check HaveCoins first. // Many methods explicitly require a CCoinsViewCache because of this method, to reduce @@ -352,7 +353,7 @@ public: const CTxOut &GetOutputFor(const CTxIn& input); private: - std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); + CCoinsMap::iterator FetchCoins(const uint256 &txid); }; #endif diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index 5b73e6051a..22f82e4259 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -1,19 +1,28 @@ -#include "bitcoin-config.h" +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + #include <cstddef> +#if defined(HAVE_SYS_SELECT_H) #include <sys/select.h> +#endif // Prior to GLIBC_2.14, memcpy was aliased to memmove. extern "C" void* memmove(void* a, const void* b, size_t c); extern "C" void* memcpy(void* a, const void* b, size_t c) { - return memmove(a, b, c); + return memmove(a, b, c); } extern "C" void __chk_fail (void) __attribute__((__noreturn__)); extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a) { - if (a >= FD_SETSIZE) - __chk_fail (); - return a / __NFDBITS; + if (a >= FD_SETSIZE) + __chk_fail (); + return a / __NFDBITS; } extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn"))); diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp new file mode 100644 index 0000000000..d93602e0fe --- /dev/null +++ b/src/compat/glibc_sanity.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include <cstddef> +#if defined(HAVE_SYS_SELECT_H) +#include <sys/select.h> +#endif + +extern "C" void* memcpy(void* a, const void* b, size_t c); +void* memcpy_int(void* a, const void* b, size_t c) +{ + return memcpy(a,b,c); +} + +namespace { +// trigger: Use the memcpy_int wrapper which calls our internal memcpy. +// A direct call to memcpy may be optimized away by the compiler. +// test: Fill an array with a sequence of integers. memcpy to a new empty array. +// Verify that the arrays are equal. Use an odd size to decrease the odds of +// the call being optimized away. +template <unsigned int T> +bool sanity_test_memcpy() +{ + unsigned int memcpy_test[T]; + unsigned int memcpy_verify[T] = {}; + for (unsigned int i = 0; i != T; ++i) + memcpy_test[i] = i; + + memcpy_int(memcpy_verify,memcpy_test,sizeof(memcpy_test)); + + for (unsigned int i = 0; i != T; ++i) + { + if(memcpy_verify[i] != i) + return false; + } + return true; +} + +#if defined(HAVE_SYS_SELECT_H) +// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined +// as >0 and optimizations must be set to at least -O2. +// test: Add a file descriptor to an empty fd_set. Verify that it has been +// correctly added. +bool sanity_test_fdelt() +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return FD_ISSET(0,&fds); +} +#endif + +} // anon namespace + +bool glibc_sanity_test() +{ +#if defined(HAVE_SYS_SELECT_H) + if (!sanity_test_fdelt()) + return false; +#endif + return sanity_test_memcpy<1025>(); +} diff --git a/src/compat/glibcxx_compat.cpp b/src/compat/glibcxx_compat.cpp index e91376f818..417166aeda 100644 --- a/src/compat/glibcxx_compat.cpp +++ b/src/compat/glibcxx_compat.cpp @@ -1,49 +1,55 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include <cstddef> #include <istream> #include <stdexcept> #include <typeinfo> #ifndef _GLIBCXX_USE_NOEXCEPT - #define _GLIBCXX_USE_NOEXCEPT throw() +#define _GLIBCXX_USE_NOEXCEPT throw() #endif namespace std { const char* bad_exception::what() const throw() { - return "std::bad_exception"; + return "std::bad_exception"; } const char* bad_cast::what() const throw() { - return "std::bad_cast"; + return "std::bad_cast"; } const char* bad_alloc::what() const throw() { - return "std::bad_alloc"; + return "std::bad_alloc"; } namespace __detail { struct _List_node_base { - void _M_hook(std::__detail::_List_node_base* const __position) throw () __attribute__((used)) - { - _M_next = __position; - _M_prev = __position->_M_prev; - __position->_M_prev->_M_next = this; - __position->_M_prev = this; - } - void _M_unhook() __attribute__((used)) - { - _List_node_base* const __next_node = _M_next; - _List_node_base* const __prev_node = _M_prev; - __prev_node->_M_next = __next_node; - __next_node->_M_prev = __prev_node; - } - _List_node_base* _M_next; - _List_node_base* _M_prev; + void _M_hook(std::__detail::_List_node_base* const __position) throw () __attribute__((used)) + { + _M_next = __position; + _M_prev = __position->_M_prev; + __position->_M_prev->_M_next = this; + __position->_M_prev = this; + } + + void _M_unhook() __attribute__((used)) + { + _List_node_base* const __next_node = _M_next; + _List_node_base* const __prev_node = _M_prev; + __prev_node->_M_next = __next_node; + __next_node->_M_prev = __prev_node; + } + + _List_node_base* _M_next; + _List_node_base* _M_prev; }; } // namespace detail @@ -61,8 +67,8 @@ out_of_range::~out_of_range() _GLIBCXX_USE_NOEXCEPT { } // Used with permission. // See: https://github.com/madlib/madlib/commit/c3db418c0d34d6813608f2137fef1012ce03043d -void -ctype<char>::_M_widen_init() const { +void ctype<char>::_M_widen_init() const +{ char __tmp[sizeof(_M_widen)]; for (unsigned __i = 0; __i < sizeof(_M_widen); ++__i) __tmp[__i] = __i; diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp new file mode 100644 index 0000000000..cd8da4fd67 --- /dev/null +++ b/src/compat/glibcxx_sanity.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <list> +#include <locale> +#include <stdexcept> + +namespace{ + +// trigger: use ctype<char>::widen to trigger ctype<char>::_M_widen_init(). +// test: convert a char from narrow to wide and back. Verify that the result +// matches the original. +bool sanity_test_widen(char testchar) +{ + const std::ctype<char>& test(std::use_facet< std::ctype<char> >(std::locale())); + return test.narrow(test.widen(testchar),'b') == testchar; +} + +// trigger: use list::push_back and list::pop_back to trigger _M_hook and +// _M_unhook. +// test: Push a sequence of integers into a list. Pop them off and verify that +// they match the original sequence. +bool sanity_test_list(unsigned int size) +{ + std::list<unsigned int> test; + for (unsigned int i = 0; i != size; ++i) + test.push_back(i+1); + + if (test.size() != size) + return false; + + while (!test.empty()) + { + if(test.back() != test.size()) + return false; + test.pop_back(); + } + return true; +} + +} // anon namespace + +// trigger: string::at(x) on an empty string to trigger __throw_out_of_range_fmt. +// test: force std::string to throw an out_of_range exception. Verify that +// it's caught correctly. +bool sanity_test_range_fmt() +{ + std::string test; + try + { + test.at(1); + } + catch (const std::out_of_range&) + { + return true; + } + catch (...){} + return false; +} + +bool glibcxx_sanity_test() +{ + return sanity_test_widen('a') && sanity_test_list(100) && sanity_test_range_fmt(); +} diff --git a/src/compat/sanity.h b/src/compat/sanity.h new file mode 100644 index 0000000000..e7df44307a --- /dev/null +++ b/src/compat/sanity.h @@ -0,0 +1,11 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCON_COMPAT_SANITY_H +#define BITCON_COMPAT_SANITY_H + +bool glibc_sanity_test(); +bool glibcxx_sanity_test(); + +#endif // BITCON_COMPAT_SANITY_H diff --git a/src/config/.empty b/src/config/.empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/config/.empty diff --git a/src/core.cpp b/src/core.cpp index 6039986e6c..b56994ecf3 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -80,9 +80,14 @@ CFeeRate::CFeeRate(int64_t nFeePaid, size_t nSize) nSatoshisPerK = 0; } -int64_t CFeeRate::GetFee(size_t nSize) +int64_t CFeeRate::GetFee(size_t nSize) const { - return nSatoshisPerK*nSize / 1000; + int64_t nFee = nSatoshisPerK*nSize / 1000; + + if (nFee == 0 && nSatoshisPerK > 0) + nFee = nSatoshisPerK; + + return nFee; } std::string CFeeRate::ToString() const @@ -91,11 +96,50 @@ std::string CFeeRate::ToString() const return result; } -uint256 CTransaction::GetHash() const +CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} + +uint256 CMutableTransaction::GetHash() const { return SerializeHash(*this); } +void CTransaction::UpdateHash() const +{ + *const_cast<uint256*>(&hash) = SerializeHash(*this); +} + +CTransaction::CTransaction() : hash(0), nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } + +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) { + UpdateHash(); +} + +CTransaction& CTransaction::operator=(const CTransaction &tx) { + *const_cast<int*>(&nVersion) = tx.nVersion; + *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin; + *const_cast<std::vector<CTxOut>*>(&vout) = tx.vout; + *const_cast<unsigned int*>(&nLockTime) = tx.nLockTime; + *const_cast<uint256*>(&hash) = tx.hash; + return *this; +} + +bool CTransaction::IsEquivalentTo(const CTransaction& tx) const +{ + if (nVersion != tx.nVersion || + nLockTime != tx.nLockTime || + vin.size() != tx.vin.size() || + vout != tx.vout) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != tx.vin[i].nSequence || + vin[i].prevout != tx.vin[i].prevout) + return false; + } + return true; +} + int64_t CTransaction::GetValueOut() const { int64_t nValueOut = 0; diff --git a/src/core.h b/src/core.h index 0e59129349..0387336c98 100644 --- a/src/core.h +++ b/src/core.h @@ -125,13 +125,14 @@ public: CFeeRate(int64_t nFeePaid, size_t nSize); CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } - int64_t GetFee(size_t size); // unit returned is satoshis - int64_t GetFeePerK() { return GetFee(1000); } // satoshis-per-1000-bytes + int64_t GetFee(size_t size) const; // unit returned is satoshis + int64_t GetFeePerK() const { return GetFee(1000); } // satoshis-per-1000-bytes friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } - + friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } + friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } std::string ToString() const; IMPLEMENT_SERIALIZE( READWRITE(nSatoshisPerK); ) @@ -203,48 +204,59 @@ public: }; +struct CMutableTransaction; + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ class CTransaction { +private: + /** Memory only. */ + const uint256 hash; + void UpdateHash() const; + public: - static CFeeRate minTxFee; - static CFeeRate minRelayTxFee; static const int CURRENT_VERSION=1; - int nVersion; - std::vector<CTxIn> vin; - std::vector<CTxOut> vout; - unsigned int nLockTime; - CTransaction() - { - SetNull(); - } + // The local variables are made const to prevent unintended modification + // without updating the cached hash value. However, CTransaction is not + // actually immutable; deserialization and assignment are implemented, + // and bypass the constness. This is safe, as they update the entire + // structure, including the hash. + const int nVersion; + const std::vector<CTxIn> vin; + const std::vector<CTxOut> vout; + const unsigned int nLockTime; - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); + /** Construct a CTransaction that qualifies as IsNull() */ + CTransaction(); + + /** Convert a CMutableTransaction into a CTransaction. */ + CTransaction(const CMutableTransaction &tx); + + CTransaction& operator=(const CTransaction& tx); + + IMPLEMENT_SERIALIZE( + READWRITE(*const_cast<int*>(&this->nVersion)); nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); + READWRITE(*const_cast<std::vector<CTxIn>*>(&vin)); + READWRITE(*const_cast<std::vector<CTxOut>*>(&vout)); + READWRITE(*const_cast<unsigned int*>(&nLockTime)); + if (fRead) + UpdateHash(); ) - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - vin.clear(); - vout.clear(); - nLockTime = 0; + bool IsNull() const { + return vin.empty() && vout.empty(); } - bool IsNull() const - { - return (vin.empty() && vout.empty()); + const uint256& GetHash() const { + return hash; } - uint256 GetHash() const; + // True if only scriptSigs are different + bool IsEquivalentTo(const CTransaction& tx) const; // Return sum of txouts. int64_t GetValueOut() const; @@ -261,22 +273,43 @@ public: friend bool operator==(const CTransaction& a, const CTransaction& b) { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); + return a.hash == b.hash; } friend bool operator!=(const CTransaction& a, const CTransaction& b) { - return !(a == b); + return a.hash != b.hash; } - std::string ToString() const; void print() const; }; +/** A mutable version of CTransaction. */ +struct CMutableTransaction +{ + int nVersion; + std::vector<CTxIn> vin; + std::vector<CTxOut> vout; + unsigned int nLockTime; + + CMutableTransaction(); + CMutableTransaction(const CTransaction& tx); + + IMPLEMENT_SERIALIZE( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + /** Compute the hash of this CMutableTransaction. This is computed on the + * fly, as opposed to GetHash() in CTransaction, which uses a cached result. + */ + uint256 GetHash() const; +}; + /** wrapper for CTxOut that provides a more compact serialization */ class CTxOutCompressor { @@ -465,12 +498,6 @@ public: uint256 BuildMerkleTree() const; - const uint256 &GetTxHash(unsigned int nIndex) const { - assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first - assert(nIndex < vtx.size()); - return vMerkleTree[nIndex]; - } - std::vector<uint256> GetMerkleBranch(int nIndex) const; static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex); void print() const; diff --git a/src/crypto/common.h b/src/crypto/common.h new file mode 100644 index 0000000000..8f675a16c5 --- /dev/null +++ b/src/crypto/common.h @@ -0,0 +1,93 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_COMMON_H +#define BITCOIN_CRYPTO_COMMON_H + +#if defined(HAVE_CONFIG_H) +#include "bitcoin-config.h" +#endif +#include <stdint.h> +#if defined(HAVE_ENDIAN_H) +#include <endian.h> +#endif + +uint32_t static inline ReadLE32(const unsigned char *ptr) { +#if HAVE_DECL_LE32TOH == 1 + return le32toh(*((uint32_t*)ptr)); +#elif !defined(WORDS_BIGENDIAN) + return *((uint32_t*)ptr); +#else + return ((uint32_t)ptr[3] << 24 | (uint32_t)ptr[2] << 16 | (uint32_t)ptr[1] << 8 | (uint32_t)ptr[0]); +#endif +} + +uint64_t static inline ReadLE64(const unsigned char *ptr) { + +#if HAVE_DECL_LE64TOH == 1 + return le64toh(*((uint64_t*)ptr)); +#elif !defined(WORDS_BIGENDIAN) + return *((uint64_t*)ptr); +#else + return ((uint64_t)ptr[7] << 56 | (uint64_t)ptr[6] << 48 | (uint64_t)ptr[5] << 40 | (uint64_t)ptr[4] << 32 | + (uint64_t)ptr[3] << 24 | (uint64_t)ptr[2] << 16 | (uint64_t)ptr[1] << 8 | (uint64_t)ptr[0]); +#endif +} + +void static inline WriteLE32(unsigned char *ptr, uint32_t x) { +#if HAVE_DECL_HTOLE32 == 1 + *((uint32_t*)ptr) = htole32(x); +#elif !defined(WORDS_BIGENDIAN) + *((uint32_t*)ptr) = x; +#else + ptr[3] = x >> 24; ptr[2] = x >> 16; ptr[1] = x >> 8; ptr[0] = x; +#endif +} + +void static inline WriteLE64(unsigned char *ptr, uint64_t x) { +#if HAVE_DECL_HTOLE64 == 1 + *((uint64_t*)ptr) = htole64(x); +#elif !defined(WORDS_BIGENDIAN) + *((uint64_t*)ptr) = x; +#else + ptr[7] = x >> 56; ptr[6] = x >> 48; ptr[5] = x >> 40; ptr[4] = x >> 32; + ptr[3] = x >> 24; ptr[2] = x >> 16; ptr[1] = x >> 8; ptr[0] = x; +#endif +} + +uint32_t static inline ReadBE32(const unsigned char *ptr) { +#if HAVE_DECL_BE32TOH == 1 + return be32toh(*((uint32_t*)ptr)); +#else + return ((uint32_t)ptr[0] << 24 | (uint32_t)ptr[1] << 16 | (uint32_t)ptr[2] << 8 | (uint32_t)ptr[3]); +#endif +} + +uint64_t static inline ReadBE64(const unsigned char *ptr) { +#if HAVE_DECL_BE64TOH == 1 + return be64toh(*((uint64_t*)ptr)); +#else + return ((uint64_t)ptr[0] << 56 | (uint64_t)ptr[1] << 48 | (uint64_t)ptr[2] << 40 | (uint64_t)ptr[3] << 32 | + (uint64_t)ptr[4] << 24 | (uint64_t)ptr[5] << 16 | (uint64_t)ptr[6] << 8 | (uint64_t)ptr[7]); +#endif +} + +void static inline WriteBE32(unsigned char *ptr, uint32_t x) { +#if HAVE_DECL_HTOBE32 == 1 + *((uint32_t*)ptr) = htobe32(x); +#else + ptr[0] = x >> 24; ptr[1] = x >> 16; ptr[2] = x >> 8; ptr[3] = x; +#endif +} + +void static inline WriteBE64(unsigned char *ptr, uint64_t x) { +#if HAVE_DECL_HTOBE64 == 1 + *((uint64_t*)ptr) = htobe64(x); +#else + ptr[0] = x >> 56; ptr[1] = x >> 48; ptr[2] = x >> 40; ptr[3] = x >> 32; + ptr[4] = x >> 24; ptr[5] = x >> 16; ptr[6] = x >> 8; ptr[7] = x; +#endif +} + +#endif diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp new file mode 100644 index 0000000000..24bd318d43 --- /dev/null +++ b/src/crypto/ripemd160.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "crypto/ripemd160.h" + +#include "crypto/common.h" +#include <string.h> + +// Internal implementation code. +namespace { + +/// Internal RIPEMD-160 implementation. +namespace ripemd160 { + +uint32_t inline f1(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } +uint32_t inline f2(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (~x & z); } +uint32_t inline f3(uint32_t x, uint32_t y, uint32_t z) { return (x | ~y) ^ z; } +uint32_t inline f4(uint32_t x, uint32_t y, uint32_t z) { return (x & z) | (y & ~z); } +uint32_t inline f5(uint32_t x, uint32_t y, uint32_t z) { return x ^ (y | ~z); } + +/** Initialize RIPEMD-160 state. */ +void inline Initialize(uint32_t *s) { + s[0] = 0x67452301ul; + s[1] = 0xEFCDAB89ul; + s[2] = 0x98BADCFEul; + s[3] = 0x10325476ul; + s[4] = 0xC3D2E1F0ul; +} + +uint32_t inline rol(uint32_t x, int i) { return (x << i) | (x >> (32-i)); } + +void inline Round(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t f, uint32_t x, uint32_t k, int r) { + a = rol(a + f + x + k, r) + e; + c = rol(c, 10); +} + +void inline R11(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } +void inline R21(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x5A827999ul, r); } +void inline R31(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6ED9EBA1ul, r); } +void inline R41(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x8F1BBCDCul, r); } +void inline R51(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0xA953FD4Eul, r); } + +void inline R12(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0x50A28BE6ul, r); } +void inline R22(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x5C4DD124ul, r); } +void inline R32(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6D703EF3ul, r); } +void inline R42(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x7A6D76E9ul, r); } +void inline R52(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } + +/** Perform a RIPEMD-160 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t *s, const unsigned char *chunk) { + uint32_t a1 = s[0], b1 = s[1], c1 = s[2], d1 = s[3], e1 = s[4]; + uint32_t a2 = a1 , b2 = b1 , c2 = c1 , d2 = d1 , e2 = e1 ; + uint32_t w0 = ReadLE32(chunk + 0), w1 = ReadLE32(chunk + 4), w2 = ReadLE32(chunk + 8), w3 = ReadLE32(chunk + 12); + uint32_t w4 = ReadLE32(chunk + 16), w5 = ReadLE32(chunk + 20), w6 = ReadLE32(chunk + 24), w7 = ReadLE32(chunk + 28); + uint32_t w8 = ReadLE32(chunk + 32), w9 = ReadLE32(chunk + 36), w10 = ReadLE32(chunk + 40), w11 = ReadLE32(chunk + 44); + uint32_t w12 = ReadLE32(chunk + 48), w13 = ReadLE32(chunk + 52), w14 = ReadLE32(chunk + 56), w15 = ReadLE32(chunk + 60); + + R11(a1, b1, c1, d1, e1, w0 , 11); R12(a2, b2, c2, d2, e2, w5 , 8); + R11(e1, a1, b1, c1, d1, w1 , 14); R12(e2, a2, b2, c2, d2, w14, 9); + R11(d1, e1, a1, b1, c1, w2 , 15); R12(d2, e2, a2, b2, c2, w7 , 9); + R11(c1, d1, e1, a1, b1, w3 , 12); R12(c2, d2, e2, a2, b2, w0 , 11); + R11(b1, c1, d1, e1, a1, w4 , 5); R12(b2, c2, d2, e2, a2, w9 , 13); + R11(a1, b1, c1, d1, e1, w5 , 8); R12(a2, b2, c2, d2, e2, w2 , 15); + R11(e1, a1, b1, c1, d1, w6 , 7); R12(e2, a2, b2, c2, d2, w11, 15); + R11(d1, e1, a1, b1, c1, w7 , 9); R12(d2, e2, a2, b2, c2, w4 , 5); + R11(c1, d1, e1, a1, b1, w8 , 11); R12(c2, d2, e2, a2, b2, w13, 7); + R11(b1, c1, d1, e1, a1, w9 , 13); R12(b2, c2, d2, e2, a2, w6 , 7); + R11(a1, b1, c1, d1, e1, w10, 14); R12(a2, b2, c2, d2, e2, w15, 8); + R11(e1, a1, b1, c1, d1, w11, 15); R12(e2, a2, b2, c2, d2, w8 , 11); + R11(d1, e1, a1, b1, c1, w12, 6); R12(d2, e2, a2, b2, c2, w1 , 14); + R11(c1, d1, e1, a1, b1, w13, 7); R12(c2, d2, e2, a2, b2, w10, 14); + R11(b1, c1, d1, e1, a1, w14, 9); R12(b2, c2, d2, e2, a2, w3 , 12); + R11(a1, b1, c1, d1, e1, w15, 8); R12(a2, b2, c2, d2, e2, w12, 6); + + R21(e1, a1, b1, c1, d1, w7 , 7); R22(e2, a2, b2, c2, d2, w6 , 9); + R21(d1, e1, a1, b1, c1, w4 , 6); R22(d2, e2, a2, b2, c2, w11, 13); + R21(c1, d1, e1, a1, b1, w13, 8); R22(c2, d2, e2, a2, b2, w3 , 15); + R21(b1, c1, d1, e1, a1, w1 , 13); R22(b2, c2, d2, e2, a2, w7 , 7); + R21(a1, b1, c1, d1, e1, w10, 11); R22(a2, b2, c2, d2, e2, w0 , 12); + R21(e1, a1, b1, c1, d1, w6 , 9); R22(e2, a2, b2, c2, d2, w13, 8); + R21(d1, e1, a1, b1, c1, w15, 7); R22(d2, e2, a2, b2, c2, w5 , 9); + R21(c1, d1, e1, a1, b1, w3 , 15); R22(c2, d2, e2, a2, b2, w10, 11); + R21(b1, c1, d1, e1, a1, w12, 7); R22(b2, c2, d2, e2, a2, w14, 7); + R21(a1, b1, c1, d1, e1, w0 , 12); R22(a2, b2, c2, d2, e2, w15, 7); + R21(e1, a1, b1, c1, d1, w9 , 15); R22(e2, a2, b2, c2, d2, w8 , 12); + R21(d1, e1, a1, b1, c1, w5 , 9); R22(d2, e2, a2, b2, c2, w12, 7); + R21(c1, d1, e1, a1, b1, w2 , 11); R22(c2, d2, e2, a2, b2, w4 , 6); + R21(b1, c1, d1, e1, a1, w14, 7); R22(b2, c2, d2, e2, a2, w9 , 15); + R21(a1, b1, c1, d1, e1, w11, 13); R22(a2, b2, c2, d2, e2, w1 , 13); + R21(e1, a1, b1, c1, d1, w8 , 12); R22(e2, a2, b2, c2, d2, w2 , 11); + + R31(d1, e1, a1, b1, c1, w3 , 11); R32(d2, e2, a2, b2, c2, w15, 9); + R31(c1, d1, e1, a1, b1, w10, 13); R32(c2, d2, e2, a2, b2, w5 , 7); + R31(b1, c1, d1, e1, a1, w14, 6); R32(b2, c2, d2, e2, a2, w1 , 15); + R31(a1, b1, c1, d1, e1, w4 , 7); R32(a2, b2, c2, d2, e2, w3 , 11); + R31(e1, a1, b1, c1, d1, w9 , 14); R32(e2, a2, b2, c2, d2, w7 , 8); + R31(d1, e1, a1, b1, c1, w15, 9); R32(d2, e2, a2, b2, c2, w14, 6); + R31(c1, d1, e1, a1, b1, w8 , 13); R32(c2, d2, e2, a2, b2, w6 , 6); + R31(b1, c1, d1, e1, a1, w1 , 15); R32(b2, c2, d2, e2, a2, w9 , 14); + R31(a1, b1, c1, d1, e1, w2 , 14); R32(a2, b2, c2, d2, e2, w11, 12); + R31(e1, a1, b1, c1, d1, w7 , 8); R32(e2, a2, b2, c2, d2, w8 , 13); + R31(d1, e1, a1, b1, c1, w0 , 13); R32(d2, e2, a2, b2, c2, w12, 5); + R31(c1, d1, e1, a1, b1, w6 , 6); R32(c2, d2, e2, a2, b2, w2 , 14); + R31(b1, c1, d1, e1, a1, w13, 5); R32(b2, c2, d2, e2, a2, w10, 13); + R31(a1, b1, c1, d1, e1, w11, 12); R32(a2, b2, c2, d2, e2, w0 , 13); + R31(e1, a1, b1, c1, d1, w5 , 7); R32(e2, a2, b2, c2, d2, w4 , 7); + R31(d1, e1, a1, b1, c1, w12, 5); R32(d2, e2, a2, b2, c2, w13, 5); + + R41(c1, d1, e1, a1, b1, w1 , 11); R42(c2, d2, e2, a2, b2, w8 , 15); + R41(b1, c1, d1, e1, a1, w9 , 12); R42(b2, c2, d2, e2, a2, w6 , 5); + R41(a1, b1, c1, d1, e1, w11, 14); R42(a2, b2, c2, d2, e2, w4 , 8); + R41(e1, a1, b1, c1, d1, w10, 15); R42(e2, a2, b2, c2, d2, w1 , 11); + R41(d1, e1, a1, b1, c1, w0 , 14); R42(d2, e2, a2, b2, c2, w3 , 14); + R41(c1, d1, e1, a1, b1, w8 , 15); R42(c2, d2, e2, a2, b2, w11, 14); + R41(b1, c1, d1, e1, a1, w12, 9); R42(b2, c2, d2, e2, a2, w15, 6); + R41(a1, b1, c1, d1, e1, w4 , 8); R42(a2, b2, c2, d2, e2, w0 , 14); + R41(e1, a1, b1, c1, d1, w13, 9); R42(e2, a2, b2, c2, d2, w5 , 6); + R41(d1, e1, a1, b1, c1, w3 , 14); R42(d2, e2, a2, b2, c2, w12, 9); + R41(c1, d1, e1, a1, b1, w7 , 5); R42(c2, d2, e2, a2, b2, w2 , 12); + R41(b1, c1, d1, e1, a1, w15, 6); R42(b2, c2, d2, e2, a2, w13, 9); + R41(a1, b1, c1, d1, e1, w14, 8); R42(a2, b2, c2, d2, e2, w9 , 12); + R41(e1, a1, b1, c1, d1, w5 , 6); R42(e2, a2, b2, c2, d2, w7 , 5); + R41(d1, e1, a1, b1, c1, w6 , 5); R42(d2, e2, a2, b2, c2, w10, 15); + R41(c1, d1, e1, a1, b1, w2 , 12); R42(c2, d2, e2, a2, b2, w14, 8); + + R51(b1, c1, d1, e1, a1, w4 , 9); R52(b2, c2, d2, e2, a2, w12, 8); + R51(a1, b1, c1, d1, e1, w0 , 15); R52(a2, b2, c2, d2, e2, w15, 5); + R51(e1, a1, b1, c1, d1, w5 , 5); R52(e2, a2, b2, c2, d2, w10, 12); + R51(d1, e1, a1, b1, c1, w9 , 11); R52(d2, e2, a2, b2, c2, w4 , 9); + R51(c1, d1, e1, a1, b1, w7 , 6); R52(c2, d2, e2, a2, b2, w1 , 12); + R51(b1, c1, d1, e1, a1, w12, 8); R52(b2, c2, d2, e2, a2, w5 , 5); + R51(a1, b1, c1, d1, e1, w2 , 13); R52(a2, b2, c2, d2, e2, w8 , 14); + R51(e1, a1, b1, c1, d1, w10, 12); R52(e2, a2, b2, c2, d2, w7 , 6); + R51(d1, e1, a1, b1, c1, w14, 5); R52(d2, e2, a2, b2, c2, w6 , 8); + R51(c1, d1, e1, a1, b1, w1 , 12); R52(c2, d2, e2, a2, b2, w2 , 13); + R51(b1, c1, d1, e1, a1, w3 , 13); R52(b2, c2, d2, e2, a2, w13, 6); + R51(a1, b1, c1, d1, e1, w8 , 14); R52(a2, b2, c2, d2, e2, w14, 5); + R51(e1, a1, b1, c1, d1, w11, 11); R52(e2, a2, b2, c2, d2, w0 , 15); + R51(d1, e1, a1, b1, c1, w6 , 8); R52(d2, e2, a2, b2, c2, w3 , 13); + R51(c1, d1, e1, a1, b1, w15, 5); R52(c2, d2, e2, a2, b2, w9 , 11); + R51(b1, c1, d1, e1, a1, w13, 6); R52(b2, c2, d2, e2, a2, w11, 11); + + uint32_t t = s[0]; + s[0] = s[1] + c1 + d2; + s[1] = s[2] + d1 + e2; + s[2] = s[3] + e1 + a2; + s[3] = s[4] + a1 + b2; + s[4] = t + b1 + c2; +} + +} // namespace ripemd160 + +} // namespace + +////// RIPEMD160 + +CRIPEMD160::CRIPEMD160() : bytes(0) { + ripemd160::Initialize(s); +} + +CRIPEMD160& CRIPEMD160::Write(const unsigned char *data, size_t len) { + const unsigned char *end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + ripemd160::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + ripemd160::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CRIPEMD160::Finalize(unsigned char hash[OUTPUT_SIZE]) { + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteLE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + WriteLE32(hash, s[0]); + WriteLE32(hash+4, s[1]); + WriteLE32(hash+8, s[2]); + WriteLE32(hash+12, s[3]); + WriteLE32(hash+16, s[4]); +} + +CRIPEMD160& CRIPEMD160::Reset() { + bytes = 0; + ripemd160::Initialize(s); + return *this; +} diff --git a/src/crypto/ripemd160.h b/src/crypto/ripemd160.h new file mode 100644 index 0000000000..44bd4879a5 --- /dev/null +++ b/src/crypto/ripemd160.h @@ -0,0 +1,27 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RIPEMD160_H +#define BITCOIN_RIPEMD160_H + +#include <stdint.h> +#include <stdlib.h> + +/** A hasher class for RIPEMD-160. */ +class CRIPEMD160 { +private: + uint32_t s[5]; + unsigned char buf[64]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 20; + + CRIPEMD160(); + CRIPEMD160& Write(const unsigned char *data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CRIPEMD160& Reset(); +}; + +#endif diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp new file mode 100644 index 0000000000..304401a50f --- /dev/null +++ b/src/crypto/sha1.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "crypto/sha1.h" + +#include "crypto/common.h" +#include <string.h> + +// Internal implementation code. +namespace { + +/// Internal SHA-1 implementation. +namespace sha1 { + +/** One round of SHA-1. */ +void inline Round(uint32_t a, uint32_t &b, uint32_t c, uint32_t d, uint32_t &e, + uint32_t f, uint32_t k, uint32_t w) { + e += ((a << 5) | (a >> 27)) + f + k + w; + b = (b << 30) | (b >> 2); +} + +uint32_t inline f1(uint32_t b, uint32_t c, uint32_t d) { return d ^ (b & (c ^ d)); } +uint32_t inline f2(uint32_t b, uint32_t c, uint32_t d) { return b ^ c ^ d; } +uint32_t inline f3(uint32_t b, uint32_t c, uint32_t d) { return (b & c) | (d & (b | c)); } + +uint32_t inline left(uint32_t x) { return (x << 1) | (x >> 31); } + +/** Initialize SHA-1 state. */ +void inline Initialize(uint32_t *s) { + s[0] = 0x67452301ul; + s[1] = 0xEFCDAB89ul; + s[2] = 0x98BADCFEul; + s[3] = 0x10325476ul; + s[4] = 0xC3D2E1F0ul; +} + +const uint32_t k1 = 0x5A827999ul; +const uint32_t k2 = 0x6ED9EBA1ul; +const uint32_t k3 = 0x8F1BBCDCul; +const uint32_t k4 = 0xCA62C1D6ul; + +/** Perform a SHA-1 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t *s, const unsigned char *chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f1(b, c, d), k1, w0 = ReadBE32(chunk + 0)); + Round(e, a, b, c, d, f1(a, b, c), k1, w1 = ReadBE32(chunk + 4)); + Round(d, e, a, b, c, f1(e, a, b), k1, w2 = ReadBE32(chunk + 8)); + Round(c, d, e, a, b, f1(d, e, a), k1, w3 = ReadBE32(chunk + 12)); + Round(b, c, d, e, a, f1(c, d, e), k1, w4 = ReadBE32(chunk + 16)); + Round(a, b, c, d, e, f1(b, c, d), k1, w5 = ReadBE32(chunk + 20)); + Round(e, a, b, c, d, f1(a, b, c), k1, w6 = ReadBE32(chunk + 24)); + Round(d, e, a, b, c, f1(e, a, b), k1, w7 = ReadBE32(chunk + 28)); + Round(c, d, e, a, b, f1(d, e, a), k1, w8 = ReadBE32(chunk + 32)); + Round(b, c, d, e, a, f1(c, d, e), k1, w9 = ReadBE32(chunk + 36)); + Round(a, b, c, d, e, f1(b, c, d), k1, w10 = ReadBE32(chunk + 40)); + Round(e, a, b, c, d, f1(a, b, c), k1, w11 = ReadBE32(chunk + 44)); + Round(d, e, a, b, c, f1(e, a, b), k1, w12 = ReadBE32(chunk + 48)); + Round(c, d, e, a, b, f1(d, e, a), k1, w13 = ReadBE32(chunk + 52)); + Round(b, c, d, e, a, f1(c, d, e), k1, w14 = ReadBE32(chunk + 56)); + Round(a, b, c, d, e, f1(b, c, d), k1, w15 = ReadBE32(chunk + 60)); + + Round(e, a, b, c, d, f1(a, b, c), k1, w0 = left(w0 ^ w13 ^ w8 ^ w2 )); + Round(d, e, a, b, c, f1(e, a, b), k1, w1 = left(w1 ^ w14 ^ w9 ^ w3 )); + Round(c, d, e, a, b, f1(d, e, a), k1, w2 = left(w2 ^ w15 ^ w10 ^ w4 )); + Round(b, c, d, e, a, f1(c, d, e), k1, w3 = left(w3 ^ w0 ^ w11 ^ w5 )); + Round(a, b, c, d, e, f2(b, c, d), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6 )); + Round(e, a, b, c, d, f2(a, b, c), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7 )); + Round(d, e, a, b, c, f2(e, a, b), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8 )); + Round(c, d, e, a, b, f2(d, e, a), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9 )); + Round(b, c, d, e, a, f2(c, d, e), k2, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(a, b, c, d, e, f2(b, c, d), k2, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(e, a, b, c, d, f2(a, b, c), k2, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(d, e, a, b, c, f2(e, a, b), k2, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(c, d, e, a, b, f2(d, e, a), k2, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(b, c, d, e, a, f2(c, d, e), k2, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(a, b, c, d, e, f2(b, c, d), k2, w14 = left(w14 ^ w11 ^ w6 ^ w0 )); + Round(e, a, b, c, d, f2(a, b, c), k2, w15 = left(w15 ^ w12 ^ w7 ^ w1 )); + + Round(d, e, a, b, c, f2(e, a, b), k2, w0 = left(w0 ^ w13 ^ w8 ^ w2 )); + Round(c, d, e, a, b, f2(d, e, a), k2, w1 = left(w1 ^ w14 ^ w9 ^ w3 )); + Round(b, c, d, e, a, f2(c, d, e), k2, w2 = left(w2 ^ w15 ^ w10 ^ w4 )); + Round(a, b, c, d, e, f2(b, c, d), k2, w3 = left(w3 ^ w0 ^ w11 ^ w5 )); + Round(e, a, b, c, d, f2(a, b, c), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6 )); + Round(d, e, a, b, c, f2(e, a, b), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7 )); + Round(c, d, e, a, b, f2(d, e, a), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8 )); + Round(b, c, d, e, a, f2(c, d, e), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9 )); + Round(a, b, c, d, e, f3(b, c, d), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(e, a, b, c, d, f3(a, b, c), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(d, e, a, b, c, f3(e, a, b), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(c, d, e, a, b, f3(d, e, a), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(b, c, d, e, a, f3(c, d, e), k3, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(a, b, c, d, e, f3(b, c, d), k3, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(e, a, b, c, d, f3(a, b, c), k3, w14 = left(w14 ^ w11 ^ w6 ^ w0 )); + Round(d, e, a, b, c, f3(e, a, b), k3, w15 = left(w15 ^ w12 ^ w7 ^ w1 )); + + Round(c, d, e, a, b, f3(d, e, a), k3, w0 = left(w0 ^ w13 ^ w8 ^ w2 )); + Round(b, c, d, e, a, f3(c, d, e), k3, w1 = left(w1 ^ w14 ^ w9 ^ w3 )); + Round(a, b, c, d, e, f3(b, c, d), k3, w2 = left(w2 ^ w15 ^ w10 ^ w4 )); + Round(e, a, b, c, d, f3(a, b, c), k3, w3 = left(w3 ^ w0 ^ w11 ^ w5 )); + Round(d, e, a, b, c, f3(e, a, b), k3, w4 = left(w4 ^ w1 ^ w12 ^ w6 )); + Round(c, d, e, a, b, f3(d, e, a), k3, w5 = left(w5 ^ w2 ^ w13 ^ w7 )); + Round(b, c, d, e, a, f3(c, d, e), k3, w6 = left(w6 ^ w3 ^ w14 ^ w8 )); + Round(a, b, c, d, e, f3(b, c, d), k3, w7 = left(w7 ^ w4 ^ w15 ^ w9 )); + Round(e, a, b, c, d, f3(a, b, c), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(d, e, a, b, c, f3(e, a, b), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(c, d, e, a, b, f3(d, e, a), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(b, c, d, e, a, f3(c, d, e), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(a, b, c, d, e, f2(b, c, d), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(e, a, b, c, d, f2(a, b, c), k4, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(d, e, a, b, c, f2(e, a, b), k4, w14 = left(w14 ^ w11 ^ w6 ^ w0 )); + Round(c, d, e, a, b, f2(d, e, a), k4, w15 = left(w15 ^ w12 ^ w7 ^ w1 )); + + Round(b, c, d, e, a, f2(c, d, e), k4, w0 = left(w0 ^ w13 ^ w8 ^ w2 )); + Round(a, b, c, d, e, f2(b, c, d), k4, w1 = left(w1 ^ w14 ^ w9 ^ w3 )); + Round(e, a, b, c, d, f2(a, b, c), k4, w2 = left(w2 ^ w15 ^ w10 ^ w4 )); + Round(d, e, a, b, c, f2(e, a, b), k4, w3 = left(w3 ^ w0 ^ w11 ^ w5 )); + Round(c, d, e, a, b, f2(d, e, a), k4, w4 = left(w4 ^ w1 ^ w12 ^ w6 )); + Round(b, c, d, e, a, f2(c, d, e), k4, w5 = left(w5 ^ w2 ^ w13 ^ w7 )); + Round(a, b, c, d, e, f2(b, c, d), k4, w6 = left(w6 ^ w3 ^ w14 ^ w8 )); + Round(e, a, b, c, d, f2(a, b, c), k4, w7 = left(w7 ^ w4 ^ w15 ^ w9 )); + Round(d, e, a, b, c, f2(e, a, b), k4, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(c, d, e, a, b, f2(d, e, a), k4, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(b, c, d, e, a, f2(c, d, e), k4, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(a, b, c, d, e, f2(b, c, d), k4, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(e, a, b, c, d, f2(a, b, c), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(d, e, a, b, c, f2(e, a, b), k4, left(w13 ^ w10 ^ w5 ^ w15)); + Round(c, d, e, a, b, f2(d, e, a), k4, left(w14 ^ w11 ^ w6 ^ w0 )); + Round(b, c, d, e, a, f2(c, d, e), k4, left(w15 ^ w12 ^ w7 ^ w1 )); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; +} + +} // namespace sha1 + +} // namespace + +////// SHA1 + +CSHA1::CSHA1() : bytes(0) { + sha1::Initialize(s); +} + +CSHA1& CSHA1::Write(const unsigned char *data, size_t len) { + const unsigned char *end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + sha1::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + sha1::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA1::Finalize(unsigned char hash[OUTPUT_SIZE]) { + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteBE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + WriteBE32(hash, s[0]); + WriteBE32(hash+4, s[1]); + WriteBE32(hash+8, s[2]); + WriteBE32(hash+12, s[3]); + WriteBE32(hash+16, s[4]); +} + +CSHA1& CSHA1::Reset() { + bytes = 0; + sha1::Initialize(s); + return *this; +} diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h new file mode 100644 index 0000000000..b16f2c88ce --- /dev/null +++ b/src/crypto/sha1.h @@ -0,0 +1,27 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SHA1_H +#define BITCOIN_SHA1_H + +#include <stdint.h> +#include <stdlib.h> + +/** A hasher class for SHA1. */ +class CSHA1 { +private: + uint32_t s[5]; + unsigned char buf[64]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 20; + + CSHA1(); + CSHA1& Write(const unsigned char *data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CSHA1& Reset(); +}; + +#endif diff --git a/src/crypto/sha2.cpp b/src/crypto/sha2.cpp new file mode 100644 index 0000000000..99a251cb12 --- /dev/null +++ b/src/crypto/sha2.cpp @@ -0,0 +1,398 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "crypto/sha2.h" + +#include "crypto/common.h" +#include <string.h> + +// Internal implementation code. +namespace { + +/// Internal SHA-256 implementation. +namespace sha256 { + +uint32_t inline Ch(uint32_t x, uint32_t y, uint32_t z) { return z ^ (x & (y ^ z)); } +uint32_t inline Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (z & (x | y)); } +uint32_t inline Sigma0(uint32_t x) { return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10); } +uint32_t inline Sigma1(uint32_t x) { return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7); } +uint32_t inline sigma0(uint32_t x) { return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3); } +uint32_t inline sigma1(uint32_t x) { return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10); } + +/** One round of SHA-256. */ +void inline Round(uint32_t a, uint32_t b, uint32_t c, uint32_t &d, + uint32_t e, uint32_t f, uint32_t g, uint32_t &h, + uint32_t k, uint32_t w) { + uint32_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w; + uint32_t t2 = Sigma0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; +} + +/** Initialize SHA-256 state. */ +void inline Initialize(uint32_t *s) { + s[0] = 0x6a09e667ul; + s[1] = 0xbb67ae85ul; + s[2] = 0x3c6ef372ul; + s[3] = 0xa54ff53aul; + s[4] = 0x510e527ful; + s[5] = 0x9b05688cul; + s[6] = 0x1f83d9abul; + s[7] = 0x5be0cd19ul; +} + +/** Perform one SHA-256 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t *s, const unsigned char *chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = ReadBE32(chunk + 0)); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = ReadBE32(chunk + 4)); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = ReadBE32(chunk + 8)); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = ReadBE32(chunk + 12)); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = ReadBE32(chunk + 16)); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = ReadBE32(chunk + 20)); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = ReadBE32(chunk + 24)); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = ReadBE32(chunk + 28)); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = ReadBE32(chunk + 32)); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = ReadBE32(chunk + 36)); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = ReadBE32(chunk + 40)); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = ReadBE32(chunk + 44)); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = ReadBE32(chunk + 48)); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = ReadBE32(chunk + 52)); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = ReadBE32(chunk + 56)); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = ReadBE32(chunk + 60)); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0( w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0( w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0( w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +} // namespace sha256 + +/// Internal SHA-512 implementation. +namespace sha512 { + +uint64_t inline Ch(uint64_t x, uint64_t y, uint64_t z) { return z ^ (x & (y ^ z)); } +uint64_t inline Maj(uint64_t x, uint64_t y, uint64_t z) { return (x & y) | (z & (x | y)); } +uint64_t inline Sigma0(uint64_t x) { return (x >> 28 | x << 36) ^ (x >> 34 | x << 30) ^ (x >> 39 | x << 25); } +uint64_t inline Sigma1(uint64_t x) { return (x >> 14 | x << 50) ^ (x >> 18 | x << 46) ^ (x >> 41 | x << 23); } +uint64_t inline sigma0(uint64_t x) { return (x >> 1 | x << 63) ^ (x >> 8 | x << 56) ^ (x >> 7); } +uint64_t inline sigma1(uint64_t x) { return (x >> 19 | x << 45) ^ (x >> 61 | x << 3) ^ (x >> 6); } + +/** One round of SHA-512. */ +void inline Round(uint64_t a, uint64_t b, uint64_t c, uint64_t &d, + uint64_t e, uint64_t f, uint64_t g, uint64_t &h, + uint64_t k, uint64_t w) { + uint64_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w; + uint64_t t2 = Sigma0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; +} + +/** Initialize SHA-256 state. */ +void inline Initialize(uint64_t *s) { + s[0] = 0x6a09e667f3bcc908ull; + s[1] = 0xbb67ae8584caa73bull; + s[2] = 0x3c6ef372fe94f82bull; + s[3] = 0xa54ff53a5f1d36f1ull; + s[4] = 0x510e527fade682d1ull; + s[5] = 0x9b05688c2b3e6c1full; + s[6] = 0x1f83d9abfb41bd6bull; + s[7] = 0x5be0cd19137e2179ull; +} + +/** Perform one SHA-512 transformation, processing a 128-byte chunk. */ +void Transform(uint64_t *s, const unsigned char *chunk) { + uint64_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint64_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98d728ae22ull, w0 = ReadBE64(chunk + 0)); + Round(h, a, b, c, d, e, f, g, 0x7137449123ef65cdull, w1 = ReadBE64(chunk + 8)); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcfec4d3b2full, w2 = ReadBE64(chunk + 16)); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba58189dbbcull, w3 = ReadBE64(chunk + 24)); + Round(e, f, g, h, a, b, c, d, 0x3956c25bf348b538ull, w4 = ReadBE64(chunk + 32)); + Round(d, e, f, g, h, a, b, c, 0x59f111f1b605d019ull, w5 = ReadBE64(chunk + 40)); + Round(c, d, e, f, g, h, a, b, 0x923f82a4af194f9bull, w6 = ReadBE64(chunk + 48)); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5da6d8118ull, w7 = ReadBE64(chunk + 56)); + Round(a, b, c, d, e, f, g, h, 0xd807aa98a3030242ull, w8 = ReadBE64(chunk + 64)); + Round(h, a, b, c, d, e, f, g, 0x12835b0145706fbeull, w9 = ReadBE64(chunk + 72)); + Round(g, h, a, b, c, d, e, f, 0x243185be4ee4b28cull, w10 = ReadBE64(chunk + 80)); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3d5ffb4e2ull, w11 = ReadBE64(chunk + 88)); + Round(e, f, g, h, a, b, c, d, 0x72be5d74f27b896full, w12 = ReadBE64(chunk + 96)); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe3b1696b1ull, w13 = ReadBE64(chunk + 104)); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a725c71235ull, w14 = ReadBE64(chunk + 112)); + Round(b, c, d, e, f, g, h, a, 0xc19bf174cf692694ull, w15 = ReadBE64(chunk + 120)); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c19ef14ad2ull, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786384f25e3ull, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc68b8cd5b5ull, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc77ac9c65ull, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f592b0275ull, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa6ea6e483ull, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dcbd41fbd4ull, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da831153b5ull, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152ee66dfabull, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d2db43210ull, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c898fb213full, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7beef0ee4ull, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf33da88fc2ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147930aa725ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351e003826full, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x142929670a0e6e70ull, w15 += sigma1(w13) + w8 + sigma0( w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a8546d22ffcull, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b21385c26c926ull, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc5ac42aedull, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d139d95b3dfull, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x650a73548baf63deull, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb3c77b2a8ull, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e47edaee6ull, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c851482353bull, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a14cf10364ull, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664bbc423001ull, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70d0f89791ull, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a30654be30ull, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819d6ef5218ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd69906245565a910ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e35855771202aull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa07032bbd1b8ull, w15 += sigma1(w13) + w8 + sigma0( w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116b8d2d0c8ull, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c085141ab53ull, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774cdf8eeb99ull, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5e19b48a8ull, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3c5c95a63ull, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4ae3418acbull, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f7763e373ull, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3d6b2b8a3ull, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee5defb2fcull, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f43172f60ull, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814a1f0ab72ull, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc702081a6439ecull, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa23631e28ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506cebde82bde9ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7b2c67915ull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2e372532bull, w15 += sigma1(w13) + w8 + sigma0( w0)); + + Round(a, b, c, d, e, f, g, h, 0xca273eceea26619cull, w0 += sigma1(w14) + w9 + sigma0( w1)); + Round(h, a, b, c, d, e, f, g, 0xd186b8c721c0c207ull, w1 += sigma1(w15) + w10 + sigma0( w2)); + Round(g, h, a, b, c, d, e, f, 0xeada7dd6cde0eb1eull, w2 += sigma1( w0) + w11 + sigma0( w3)); + Round(f, g, h, a, b, c, d, e, 0xf57d4f7fee6ed178ull, w3 += sigma1( w1) + w12 + sigma0( w4)); + Round(e, f, g, h, a, b, c, d, 0x06f067aa72176fbaull, w4 += sigma1( w2) + w13 + sigma0( w5)); + Round(d, e, f, g, h, a, b, c, 0x0a637dc5a2c898a6ull, w5 += sigma1( w3) + w14 + sigma0( w6)); + Round(c, d, e, f, g, h, a, b, 0x113f9804bef90daeull, w6 += sigma1( w4) + w15 + sigma0( w7)); + Round(b, c, d, e, f, g, h, a, 0x1b710b35131c471bull, w7 += sigma1( w5) + w0 + sigma0( w8)); + Round(a, b, c, d, e, f, g, h, 0x28db77f523047d84ull, w8 += sigma1( w6) + w1 + sigma0( w9)); + Round(h, a, b, c, d, e, f, g, 0x32caab7b40c72493ull, w9 += sigma1( w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x3c9ebe0a15c9bebcull, w10 += sigma1( w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x431d67c49c100d4cull, w11 += sigma1( w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x4cc5d4becb3e42b6ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0x597f299cfc657e2aull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x5fcb6fab3ad6faecull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x6c44198c4a475817ull, w15 += sigma1(w13) + w8 + sigma0( w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +} // namespace sha512 + +} // namespace + + +////// SHA-256 + +CSHA256::CSHA256() : bytes(0) { + sha256::Initialize(s); +} + +CSHA256& CSHA256::Write(const unsigned char *data, size_t len) { + const unsigned char *end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + sha256::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + sha256::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE]) { + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteBE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + WriteBE32(hash, s[0]); + WriteBE32(hash+4, s[1]); + WriteBE32(hash+8, s[2]); + WriteBE32(hash+12, s[3]); + WriteBE32(hash+16, s[4]); + WriteBE32(hash+20, s[5]); + WriteBE32(hash+24, s[6]); + WriteBE32(hash+28, s[7]); +} + +CSHA256& CSHA256::Reset() { + bytes = 0; + sha256::Initialize(s); + return *this; +} + +////// SHA-512 + +CSHA512::CSHA512() : bytes(0) { + sha512::Initialize(s); +} + +CSHA512& CSHA512::Write(const unsigned char *data, size_t len) { + const unsigned char *end = data + len; + size_t bufsize = bytes % 128; + if (bufsize && bufsize + len >= 128) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 128 - bufsize); + bytes += 128 - bufsize; + data += 128 - bufsize; + sha512::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 128) { + // Process full chunks directly from the source. + sha512::Transform(s, data); + data += 128; + bytes += 128; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) { + static const unsigned char pad[128] = {0x80}; + unsigned char sizedesc[16] = {0x00}; + WriteBE64(sizedesc+8, bytes << 3); + Write(pad, 1 + ((239 - (bytes % 128)) % 128)); + Write(sizedesc, 16); + WriteBE64(hash, s[0]); + WriteBE64(hash+8, s[1]); + WriteBE64(hash+16, s[2]); + WriteBE64(hash+24, s[3]); + WriteBE64(hash+32, s[4]); + WriteBE64(hash+40, s[5]); + WriteBE64(hash+48, s[6]); + WriteBE64(hash+56, s[7]); +} + +CSHA512& CSHA512::Reset() { + bytes = 0; + sha512::Initialize(s); + return *this; +} + +////// HMAC-SHA-512 + +CHMAC_SHA512::CHMAC_SHA512(const unsigned char *key, size_t keylen) { + unsigned char rkey[128]; + if (keylen <= 128) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 128 - keylen); + } else { + CSHA512().Write(key, keylen).Finalize(rkey); + memset(rkey + 64, 0, 64); + } + + for (int n=0; n<128; n++) + rkey[n] ^= 0x5c; + outer.Write(rkey, 128); + + for (int n=0; n<128; n++) + rkey[n] ^= 0x5c ^ 0x36; + inner.Write(rkey, 128); +} + +void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) { + unsigned char temp[64]; + inner.Finalize(temp); + outer.Write(temp, 64).Finalize(hash); +} diff --git a/src/crypto/sha2.h b/src/crypto/sha2.h new file mode 100644 index 0000000000..088d5e194c --- /dev/null +++ b/src/crypto/sha2.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SHA2_H +#define BITCOIN_SHA2_H + +#include <stdint.h> +#include <stdlib.h> + +/** A hasher class for SHA-256. */ +class CSHA256 { +private: + uint32_t s[8]; + unsigned char buf[64]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 32; + + CSHA256(); + CSHA256& Write(const unsigned char *data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CSHA256& Reset(); +}; + +/** A hasher class for SHA-512. */ +class CSHA512 { +private: + uint64_t s[8]; + unsigned char buf[128]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 64; + + CSHA512(); + CSHA512& Write(const unsigned char *data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CSHA512& Reset(); +}; + +/** A hasher class for HMAC-SHA-512. */ +class CHMAC_SHA512 { +private: + CSHA512 outer; + CSHA512 inner; + +public: + static const size_t OUTPUT_SIZE = 64; + + CHMAC_SHA512(const unsigned char *key, size_t keylen); + CHMAC_SHA512& Write(const unsigned char *data, size_t len) { + inner.Write(data, len); + return *this; + } + void Finalize(unsigned char hash[OUTPUT_SIZE]); +}; + +#endif diff --git a/src/hash.cpp b/src/hash.cpp index 7b054bd154..bddd8abf38 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -56,44 +56,3 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char return h1; } - -int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len) -{ - unsigned char key[128]; - if (len <= 128) - { - memcpy(key, pkey, len); - memset(key + len, 0, 128-len); - } - else - { - SHA512_CTX ctxKey; - SHA512_Init(&ctxKey); - SHA512_Update(&ctxKey, pkey, len); - SHA512_Final(key, &ctxKey); - memset(key + 64, 0, 64); - } - - for (int n=0; n<128; n++) - key[n] ^= 0x5c; - SHA512_Init(&pctx->ctxOuter); - SHA512_Update(&pctx->ctxOuter, key, 128); - - for (int n=0; n<128; n++) - key[n] ^= 0x5c ^ 0x36; - SHA512_Init(&pctx->ctxInner); - return SHA512_Update(&pctx->ctxInner, key, 128); -} - -int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len) -{ - return SHA512_Update(&pctx->ctxInner, pdata, len); -} - -int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx) -{ - unsigned char buf[64]; - SHA512_Final(buf, &pctx->ctxInner); - SHA512_Update(&pctx->ctxOuter, buf, 64); - return SHA512_Final(pmd, &pctx->ctxOuter); -} diff --git a/src/hash.h b/src/hash.h index 7dbf1b6448..f2a0ebfe1f 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,55 +6,138 @@ #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H +#include "crypto/sha2.h" +#include "crypto/ripemd160.h" #include "serialize.h" #include "uint256.h" #include "version.h" #include <vector> -#include <openssl/ripemd.h> -#include <openssl/sha.h> +/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */ +class CHash256 { +private: + CSHA256 sha; +public: + static const size_t OUTPUT_SIZE = CSHA256::OUTPUT_SIZE; + + void Finalize(unsigned char hash[OUTPUT_SIZE]) { + unsigned char buf[sha.OUTPUT_SIZE]; + sha.Finalize(buf); + sha.Reset().Write(buf, sha.OUTPUT_SIZE).Finalize(hash); + } + + CHash256& Write(const unsigned char *data, size_t len) { + sha.Write(data, len); + return *this; + } + + CHash256& Reset() { + sha.Reset(); + return *this; + } +}; +/** A hasher class for Bitcoin's 160-bit hash (SHA-256 + RIPEMD-160). */ +class CHash160 { +private: + CSHA256 sha; +public: + static const size_t OUTPUT_SIZE = CRIPEMD160::OUTPUT_SIZE; + + void Finalize(unsigned char hash[OUTPUT_SIZE]) { + unsigned char buf[sha.OUTPUT_SIZE]; + sha.Finalize(buf); + CRIPEMD160().Write(buf, sha.OUTPUT_SIZE).Finalize(hash); + } + + CHash160& Write(const unsigned char *data, size_t len) { + sha.Write(data, len); + return *this; + } + + CHash160& Reset() { + sha.Reset(); + return *this; + } +}; + +/** Compute the 256-bit hash of an object. */ template<typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend) { - static unsigned char pblank[1]; - uint256 hash1; - SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; + static const unsigned char pblank[1] = {}; + uint256 result; + CHash256().Write(pbegin == pend ? pblank : (const unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0])) + .Finalize((unsigned char*)&result); + return result; +} + +/** Compute the 256-bit hash of the concatenation of two objects. */ +template<typename T1, typename T2> +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) { + static const unsigned char pblank[1] = {}; + uint256 result; + CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])) + .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])) + .Finalize((unsigned char*)&result); + return result; +} + +/** Compute the 256-bit hash of the concatenation of three objects. */ +template<typename T1, typename T2, typename T3> +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) { + static const unsigned char pblank[1] = {}; + uint256 result; + CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])) + .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])) + .Write(p3begin == p3end ? pblank : (const unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0])) + .Finalize((unsigned char*)&result); + return result; +} + +/** Compute the 160-bit hash an object. */ +template<typename T1> +inline uint160 Hash160(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1] = {}; + uint160 result; + CHash160().Write(pbegin == pend ? pblank : (const unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0])) + .Finalize((unsigned char*)&result); + return result; } +/** Compute the 160-bit hash of a vector. */ +inline uint160 Hash160(const std::vector<unsigned char>& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + +/** A writer stream (for serialization) that computes a 256-bit hash. */ class CHashWriter { private: - SHA256_CTX ctx; + CHash256 ctx; public: int nType; int nVersion; - void Init() { - SHA256_Init(&ctx); - } - - CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { - Init(); - } + CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {} CHashWriter& write(const char *pch, size_t size) { - SHA256_Update(&ctx, pch, size); + ctx.Write((const unsigned char*)pch, size); return (*this); } // invalidates the object uint256 GetHash() { - uint256 hash1; - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; + uint256 result; + ctx.Finalize((unsigned char*)&result); + return result; } template<typename T> @@ -65,41 +148,7 @@ public: } }; - -template<typename T1, typename T2> -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -template<typename T1, typename T2, typename T3> -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); - SHA256_Final((unsigned char*)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - +/** Compute the 256-bit hash of an object's serialization. */ template<typename T> uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) { @@ -108,32 +157,6 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL return ss.GetHash(); } -template<typename T1> -inline uint160 Hash160(const T1 pbegin, const T1 pend) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); - uint160 hash2; - RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); - return hash2; -} - -inline uint160 Hash160(const std::vector<unsigned char>& vch) -{ - return Hash160(vch.begin(), vch.end()); -} - unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash); -typedef struct -{ - SHA512_CTX ctxInner; - SHA512_CTX ctxOuter; -} HMAC_SHA512_CTX; - -int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len); -int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len); -int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx); - #endif diff --git a/src/init.cpp b/src/init.cpp index 39453da9c8..f9e568a207 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "init.h" @@ -31,6 +31,7 @@ #ifndef WIN32 #include <signal.h> #endif +#include "compat/sanity.h" #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> @@ -61,6 +62,7 @@ enum BindFlags { }; static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; +CClientUIInterface uiInterface; ////////////////////////////////////////////////////////////////////////////// // @@ -115,7 +117,6 @@ void Shutdown() RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); StopRPCThreads(); - ShutdownRPCMining(); #ifdef ENABLE_WALLET if (pwalletMain) bitdb.Flush(false); @@ -195,8 +196,9 @@ bool static Bind(const CService &addr, unsigned int flags) { return true; } -std::string HelpMessage(HelpMessageMode hmm) +std::string HelpMessage(HelpMessageMode mode) { + // When adding new options to the categories, please keep and ensure alphabetical ordering. string strUsage = _("Options:") + "\n"; strUsage += " -? " + _("This help message") + "\n"; strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; @@ -204,7 +206,7 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; strUsage += " -checklevel=<n> " + _("How thorough the block verification of -checkblocks is (0-4, default: 3)") + "\n"; strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; - if (hmm == HMM_BITCOIND) + if (mode == HMM_BITCOIND) { #if !defined(WIN32) strUsage += " -daemon " + _("Run in the background as a daemon and accept commands") + "\n"; @@ -237,9 +239,8 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n"; strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; - strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS proxy") + "\n"; + strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n"; strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; - strUsage += " -socks=<n> " + _("Select SOCKS version for -proxy (4 or 5, default: 5)") + "\n"; strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; #ifdef USE_UPNP #if USE_UPNP @@ -252,14 +253,19 @@ std::string HelpMessage(HelpMessageMode hmm) #ifdef ENABLE_WALLET strUsage += "\n" + _("Wallet options:") + "\n"; strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n"; - strUsage += " -paytxfee=<amt> " + _("Fee per kB to add to transactions you send") + "\n"; + if (GetBoolArg("-help-debug", false)) + strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n"; + strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n"; strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + " " + _("on startup") + "\n"; + strUsage += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\n"; strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup") + "\n"; strUsage += " -spendzeroconfchange " + _("Spend unconfirmed change when sending transactions (default: 1)") + "\n"; + strUsage += " -txconfirmtarget=<n> " + _("If paytxfee is not set, include enough fee so transactions are confirmed on average within n blocks (default: 1)") + "\n"; strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n"; strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + _("(default: wallet.dat)") + "\n"; strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n"; + strUsage += " -zapwallettxes=<mode> " + _("Delete all wallet transactions and only recover those part of the blockchain through -rescan on startup") + "\n"; + strUsage += " " + _("(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)") + "\n"; #endif strUsage += "\n" + _("Debugging/Testing options:") + "\n"; @@ -273,25 +279,26 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -dropmessagestest=<n> " + _("Randomly drop 1 of every <n> network messages") + "\n"; strUsage += " -fuzzmessagestest=<n> " + _("Randomly fuzz 1 of every <n> network messages") + "\n"; strUsage += " -flushwallet " + _("Run a thread to flush wallet periodically (default: 1)") + "\n"; + strUsage += " -stopafterblockimport " + _("Stop running after importing blocks from disk (default: 0)") + "\n"; } strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n"; strUsage += " " + _("If <category> is not supplied, output all debugging information.") + "\n"; strUsage += " " + _("<category> can be:"); strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below - if (hmm == HMM_BITCOIN_QT) + if (mode == HMM_BITCOIN_QT) strUsage += ", qt"; strUsage += ".\n"; strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n"; strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n"; + strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n"; strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; if (GetBoolArg("-help-debug", false)) { strUsage += " -limitfreerelay=<n> " + _("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default:15)") + "\n"; strUsage += " -maxsigcachesize=<n> " + _("Limit size of signature cache to <n> entries (default: 50000)") + "\n"; } - strUsage += " -mintxfee=<amt> " + _("Fees smaller than this are considered zero fee (for transaction creation) (default:") + " " + FormatMoney(CTransaction::minTxFee.GetFeePerK()) + ")" + "\n"; - strUsage += " -minrelaytxfee=<amt> " + _("Fees smaller than this are considered zero fee (for relaying) (default:") + " " + FormatMoney(CTransaction::minRelayTxFee.GetFeePerK()) + ")" + "\n"; + strUsage += " -minrelaytxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK())) + "\n"; strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; if (GetBoolArg("-help-debug", false)) { @@ -306,6 +313,8 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; strUsage += " -testnet " + _("Use the test network") + "\n"; + strUsage += "\n" + _("Node relay options:") + "\n"; + strUsage += " -datacarrier " + _("Relay and mine data carrier transactions (default: 1)") + "\n"; strUsage += "\n" + _("Block creation options:") + "\n"; strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; strUsage += " -blockmaxsize=<n> " + strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE) + "\n"; @@ -317,7 +326,7 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; - strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address. This option can be specified multiple times") + "\n"; + strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times") + "\n"; strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; strUsage += "\n" + _("RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; @@ -404,6 +413,11 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) LogPrintf("Warning: Could not open blocks file %s\n", path.string()); } } + + if (GetBoolArg("-stopafterblockimport", false)) { + LogPrintf("Stopping after block import\n"); + StartShutdown(); + } } /** Sanity checks @@ -417,8 +431,8 @@ bool InitSanityCheck(void) "information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries"); return false; } - - // TODO: remaining sanity checks, see #4081 + if (!glibc_sanity_test() || !glibcxx_sanity_test()) + return false; return true; } @@ -529,7 +543,7 @@ bool AppInit2(boost::thread_group& threadGroup) // -zapwallettx implies a rescan if (GetBoolArg("-zapwallettxes", false)) { if (SoftSetBoolArg("-rescan", true)) - LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); + LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n"); } // Make sure enough file descriptors are available @@ -553,6 +567,12 @@ bool AppInit2(boost::thread_group& threadGroup) // Check for -debugnet (deprecated) if (GetBoolArg("-debugnet", false)) InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net")); + // Check for -socks - as this is a privacy risk to continue, exit here + if (mapArgs.count("-socks")) + return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); + // Check for -tor - as this is a privacy risk to continue, exit here + if (GetBoolArg("-tor", false)) + return InitError(_("Error: Unsupported argument -tor found, use -onion.")); fBenchmark = GetBoolArg("-benchmark", false); // Checkmempool defaults to true in regtest mode @@ -571,6 +591,7 @@ bool AppInit2(boost::thread_group& threadGroup) fServer = GetBoolArg("-server", false); fPrintToConsole = GetBoolArg("-printtoconsole", false); fLogTimestamps = GetBoolArg("-logtimestamps", true); + fLogIPs = GetBoolArg("-logips", false); setvbuf(stdout, NULL, _IOLBF, 0); #ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); @@ -595,24 +616,24 @@ bool AppInit2(boost::thread_group& threadGroup) // a transaction spammer can cheaply fill blocks using // 1-satoshi-fee transactions. It should be set above the real // cost to you of processing a transaction. - if (mapArgs.count("-mintxfee")) - { - int64_t n = 0; - if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) - CTransaction::minTxFee = CFeeRate(n); - else - return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"])); - } if (mapArgs.count("-minrelaytxfee")) { int64_t n = 0; if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) - CTransaction::minRelayTxFee = CFeeRate(n); + ::minRelayTxFee = CFeeRate(n); else return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"])); } #ifdef ENABLE_WALLET + if (mapArgs.count("-mintxfee")) + { + int64_t n = 0; + if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) + CWallet::minTxFee = CFeeRate(n); + else + return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"])); + } if (mapArgs.count("-paytxfee")) { int64_t nFeePerK = 0; @@ -621,7 +642,13 @@ bool AppInit2(boost::thread_group& threadGroup) if (nFeePerK > nHighTransactionFeeWarning) InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), + mapArgs["-paytxfee"], ::minRelayTxFee.ToString())); + } } + nTxConfirmTarget = GetArg("-txconfirmtarget", 1); bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); @@ -722,10 +749,6 @@ bool AppInit2(boost::thread_group& threadGroup) RegisterNodeSignals(GetNodeSignals()); - int nSocksVersion = GetArg("-socks", 5); - if (nSocksVersion != 4 && nSocksVersion != 5) - return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); - if (mapArgs.count("-onlynet")) { std::set<enum Network> nets; BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { @@ -749,30 +772,24 @@ bool AppInit2(boost::thread_group& threadGroup) return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); if (!IsLimited(NET_IPV4)) - SetProxy(NET_IPV4, addrProxy, nSocksVersion); - if (nSocksVersion > 4) { - if (!IsLimited(NET_IPV6)) - SetProxy(NET_IPV6, addrProxy, nSocksVersion); - SetNameProxy(addrProxy, nSocksVersion); - } + SetProxy(NET_IPV4, addrProxy); + if (!IsLimited(NET_IPV6)) + SetProxy(NET_IPV6, addrProxy); + SetNameProxy(addrProxy); fProxy = true; } // -onion can override normal proxy, -noonion disables tor entirely - // -tor here is a temporary backwards compatibility measure - if (mapArgs.count("-tor")) - printf("Notice: option -tor has been replaced with -onion and will be removed in a later version.\n"); if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") && - !(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && - (fProxy || mapArgs.count("-onion") || mapArgs.count("-tor"))) { + (fProxy || mapArgs.count("-onion"))) { CService addrOnion; - if (!mapArgs.count("-onion") && !mapArgs.count("-tor")) + if (!mapArgs.count("-onion")) addrOnion = addrProxy; else - addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050); + addrOnion = CService(mapArgs["-onion"], 9050); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"]:mapArgs["-tor"])); - SetProxy(NET_TOR, addrOnion, 5); + return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"])); + SetProxy(NET_TOR, addrOnion); SetReachable(NET_TOR); } @@ -986,11 +1003,15 @@ bool AppInit2(boost::thread_group& threadGroup) pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); } else { + + // needed to restore wallet transaction meta data after -zapwallettxes + std::vector<CWalletTx> vWtx; + if (GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); pwalletMain = new CWallet(strWalletFile); - DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); return false; @@ -1085,6 +1106,29 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; + + // Restore wallet transaction metadata after -zapwallettxes=1 + if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + { + BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) + { + uint256 hash = wtxOld.GetHash(); + std::map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash); + if (mi != pwalletMain->mapWallet.end()) + { + const CWalletTx* copyFrom = &wtxOld; + CWalletTx* copyTo = &mi->second; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + copyTo->nTimeReceived = copyFrom->nTimeReceived; + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + copyTo->nOrderPos = copyFrom->nOrderPos; + copyTo->WriteToDisk(); + } + } + } } } // (!fDisableWallet) #else // ENABLE_WALLET @@ -1139,9 +1183,8 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif + InitRespendFilter(); StartNode(threadGroup); - // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. - InitRPCMining(); if (fServer) StartRPCThreads(); diff --git a/src/init.h b/src/init.h index 52daa47616..626525c9ad 100644 --- a/src/init.h +++ b/src/init.h @@ -12,7 +12,7 @@ class CWallet; namespace boost { class thread_group; -}; +} // namespace boost extern CWallet* pwalletMain; diff --git a/src/key.cpp b/src/key.cpp index 4747beffb4..3c4fa77e72 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -4,14 +4,35 @@ #include "key.h" +#include "crypto/sha2.h" +#include <openssl/rand.h> + +#ifdef USE_SECP256K1 +#include <secp256k1.h> +#else #include <openssl/bn.h> #include <openssl/ecdsa.h> #include <openssl/obj_mac.h> -#include <openssl/rand.h> +#endif // anonymous namespace with local implementation code (OpenSSL interaction) namespace { +#ifdef USE_SECP256K1 +#include <secp256k1.h> +class CSecp256k1Init { +public: + CSecp256k1Init() { + secp256k1_start(); + } + ~CSecp256k1Init() { + secp256k1_stop(); + } +}; +static CSecp256k1Init instance_of_csecp256k1; + +#else + // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { @@ -332,6 +353,8 @@ public: } }; +#endif + int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { while (c1len > c2len) { if (*c1) @@ -375,8 +398,7 @@ const unsigned char vchMaxModHalfOrder[32] = { const unsigned char vchZero[0] = {}; - -}; // end of anonymous namespace +} // anon namespace bool CKey::Check(const unsigned char *vch) { return CompareBigEndian(vch, 32, vchZero, 0) > 0 && @@ -397,10 +419,15 @@ void CKey::MakeNewKey(bool fCompressedIn) { } bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size())) + return false; +#else CECKey key; if (!key.SetPrivKey(privkey)) return false; key.GetSecretBytes(vch); +#endif fCompressed = fCompressedIn; fValid = true; return true; @@ -408,99 +435,167 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { CPrivKey CKey::GetPrivKey() const { assert(fValid); + CPrivKey privkey; +#ifdef USE_SECP256K1 + privkey.resize(279); + int privkeylen = 279; + int ret = secp256k1_ecdsa_privkey_export(begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed); + assert(ret); + privkey.resize(privkeylen); +#else CECKey key; key.SetSecretBytes(vch); - CPrivKey privkey; key.GetPrivKey(privkey, fCompressed); +#endif return privkey; } CPubKey CKey::GetPubKey() const { assert(fValid); + CPubKey pubkey; +#ifdef USE_SECP256K1 + int clen = 65; + int ret = secp256k1_ecdsa_pubkey_create((unsigned char*)pubkey.begin(), &clen, begin(), fCompressed); + assert(ret); + assert(pubkey.IsValid()); + assert((int)pubkey.size() == clen); +#else CECKey key; key.SetSecretBytes(vch); - CPubKey pubkey; key.GetPubKey(pubkey, fCompressed); +#endif return pubkey; } bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const { if (!fValid) return false; +#ifdef USE_SECP256K1 + vchSig.resize(72); + int nSigLen = 72; + CKey nonce; + do { + nonce.MakeNewKey(true); + if (secp256k1_ecdsa_sign((const unsigned char*)&hash, 32, (unsigned char*)&vchSig[0], &nSigLen, begin(), nonce.begin())) + break; + } while(true); + vchSig.resize(nSigLen); + return true; +#else CECKey key; key.SetSecretBytes(vch); return key.Sign(hash, vchSig); +#endif } bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const { if (!fValid) return false; - CECKey key; - key.SetSecretBytes(vch); vchSig.resize(65); int rec = -1; +#ifdef USE_SECP256K1 + CKey nonce; + do { + nonce.MakeNewKey(true); + if (secp256k1_ecdsa_sign_compact((const unsigned char*)&hash, 32, &vchSig[1], begin(), nonce.begin(), &rec)) + break; + } while(true); +#else + CECKey key; + key.SetSecretBytes(vch); if (!key.SignCompact(hash, &vchSig[1], rec)) return false; +#endif assert(rec != -1); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); return true; } bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size())) + return false; +#else CECKey key; if (!key.SetPrivKey(privkey, fSkipCheck)) return false; - key.GetSecretBytes(vch); +#endif fCompressed = vchPubKey.IsCompressed(); fValid = true; - + if (fSkipCheck) return true; - + if (GetPubKey() != vchPubKey) return false; - + return true; } bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { if (!IsValid()) return false; +#ifdef USE_SECP256K1 + if (secp256k1_ecdsa_verify((const unsigned char*)&hash, 32, &vchSig[0], vchSig.size(), begin(), size()) != 1) + return false; +#else CECKey key; if (!key.SetPubKey(*this)) return false; if (!key.Verify(hash, vchSig)) return false; +#endif return true; } bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) { if (vchSig.size() != 65) return false; + int recid = (vchSig[0] - 27) & 3; + bool fComp = (vchSig[0] - 27) & 4; +#ifdef USE_SECP256K1 + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact((const unsigned char*)&hash, 32, &vchSig[1], (unsigned char*)begin(), &pubkeylen, fComp, recid)) + return false; + assert((int)size() == pubkeylen); +#else CECKey key; - if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) + if (!key.Recover(hash, &vchSig[1], recid)) return false; - key.GetPubKey(*this, (vchSig[0] - 27) & 4); + key.GetPubKey(*this, fComp); +#endif return true; } bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_pubkey_verify(begin(), size())) + return false; +#else CECKey key; if (!key.SetPubKey(*this)) return false; +#endif return true; } bool CPubKey::Decompress() { if (!IsValid()) return false; +#ifdef USE_SECP256K1 + int clen = size(); + int ret = secp256k1_ecdsa_pubkey_decompress((unsigned char*)begin(), &clen); + assert(ret); + assert(clen == (int)size()); +#else CECKey key; if (!key.SetPubKey(*this)) return false; key.GetPubKey(*this, false); +#endif return true; } @@ -510,12 +605,10 @@ void static BIP32Hash(const unsigned char chainCode[32], unsigned int nChild, un num[1] = (nChild >> 16) & 0xFF; num[2] = (nChild >> 8) & 0xFF; num[3] = (nChild >> 0) & 0xFF; - HMAC_SHA512_CTX ctx; - HMAC_SHA512_Init(&ctx, chainCode, 32); - HMAC_SHA512_Update(&ctx, &header, 1); - HMAC_SHA512_Update(&ctx, data, 32); - HMAC_SHA512_Update(&ctx, num, 4); - HMAC_SHA512_Final(output, &ctx); + CHMAC_SHA512(chainCode, 32).Write(&header, 1) + .Write(data, 32) + .Write(num, 4) + .Finalize(output); } bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const { @@ -532,7 +625,12 @@ bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild BIP32Hash(cc, nChild, 0, begin(), out); } memcpy(ccChild, out+32, 32); +#ifdef USE_SECP256K1 + memcpy((unsigned char*)keyChild.begin(), begin(), 32); + bool ret = secp256k1_ecdsa_privkey_tweak_add((unsigned char*)keyChild.begin(), out); +#else bool ret = CECKey::TweakSecret((unsigned char*)keyChild.begin(), begin(), out); +#endif UnlockObject(out); keyChild.fCompressed = true; keyChild.fValid = ret; @@ -546,10 +644,15 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned i unsigned char out[64]; BIP32Hash(cc, nChild, *begin(), begin()+1, out); memcpy(ccChild, out+32, 32); +#ifdef USE_SECP256K1 + pubkeyChild = *this; + bool ret = secp256k1_ecdsa_pubkey_tweak_add((unsigned char*)pubkeyChild.begin(), pubkeyChild.size(), out); +#else CECKey key; bool ret = key.SetPubKey(*this); ret &= key.TweakPublic(out); key.GetPubKey(pubkeyChild, true); +#endif return ret; } @@ -562,13 +665,10 @@ bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const { } void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { - static const char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; - HMAC_SHA512_CTX ctx; - HMAC_SHA512_Init(&ctx, hashkey, sizeof(hashkey)); - HMAC_SHA512_Update(&ctx, seed, nSeedLen); + static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; unsigned char out[64]; LockObject(out); - HMAC_SHA512_Final(out, &ctx); + CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out); key.Set(&out[0], &out[32], true); memcpy(vchChainCode, &out[32], 32); UnlockObject(out); @@ -633,6 +733,9 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { } bool ECC_InitSanityCheck() { +#ifdef USE_SECP256K1 + return true; +#else EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); if(pkey == NULL) return false; @@ -640,6 +743,7 @@ bool ECC_InitSanityCheck() { // TODO Is there more EC functionality that could be missing? return true; +#endif } diff --git a/src/keystore.cpp b/src/keystore.cpp index 594e0c61da..2a4c88d565 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -59,3 +59,15 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } +bool CBasicKeyStore::AddWatchOnly(const CScript &dest) +{ + LOCK(cs_KeyStore); + setWatchOnly.insert(dest); + return true; +} + +bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const +{ + LOCK(cs_KeyStore); + return setWatchOnly.count(dest) > 0; +} diff --git a/src/keystore.h b/src/keystore.h index 79d8661aca..72411a1387 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -8,11 +8,21 @@ #include "key.h" #include "sync.h" +#include "script.h" // for CNoDestination #include <boost/signals2/signal.hpp> +#include <boost/variant.hpp> class CScript; +/** A txout script template with a specific destination. It is either: + * * CNoDestination: no destination set + * * CKeyID: TX_PUBKEYHASH destination + * * CScriptID: TX_SCRIPTHASH destination + * A CTxDestination is the internal data type encoded in a CBitcoinAddress + */ +typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination; + /** A virtual base class for key stores */ class CKeyStore { @@ -36,10 +46,15 @@ public: virtual bool AddCScript(const CScript& redeemScript) =0; virtual bool HaveCScript(const CScriptID &hash) const =0; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; + + // Support for Watch-only addresses + virtual bool AddWatchOnly(const CScript &dest) =0; + virtual bool HaveWatchOnly(const CScript &dest) const =0; }; typedef std::map<CKeyID, CKey> KeyMap; typedef std::map<CScriptID, CScript > ScriptMap; +typedef std::set<CScript> WatchOnlySet; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore @@ -47,6 +62,7 @@ class CBasicKeyStore : public CKeyStore protected: KeyMap mapKeys; ScriptMap mapScripts; + WatchOnlySet setWatchOnly; public: bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); @@ -88,6 +104,9 @@ public: virtual bool AddCScript(const CScript& redeemScript); virtual bool HaveCScript(const CScriptID &hash) const; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; + + virtual bool AddWatchOnly(const CScript &dest); + virtual bool HaveWatchOnly(const CScript &dest) const; }; typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; diff --git a/src/main.cpp b/src/main.cpp index 98e1741954..a9c080ffae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,11 +7,13 @@ #include "addrman.h" #include "alert.h" +#include "bloom.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" #include "init.h" #include "net.h" +#include "pow.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" @@ -36,8 +38,6 @@ using namespace boost; CCriticalSection cs_main; -CTxMemPool mempool; - map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; int64_t nTimeBestReceived = 0; @@ -48,10 +48,10 @@ bool fBenchmark = false; bool fTxIndex = false; unsigned int nCoinCacheSize = 5000; -/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ -CFeeRate CTransaction::minTxFee = CFeeRate(10000); // Override with -mintxfee /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ -CFeeRate CTransaction::minRelayTxFee = CFeeRate(1000); +CFeeRate minRelayTxFee = CFeeRate(1000); + +CTxMemPool mempool(::minRelayTxFee); struct COrphanBlock { uint256 hashBlock; @@ -71,6 +71,7 @@ const string strMessageMagic = "Bitcoin Signed Message:\n"; // Internal stuff namespace { + struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) { @@ -119,8 +120,18 @@ namespace { }; map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; map<uint256, pair<NodeId, list<uint256>::iterator> > mapBlocksToDownload; + +} // anon namespace + +// Bloom filter to limit respend relays to one +static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000; +static CBloomFilter doubleSpendFilter; +void InitRespendFilter() { + seed_insecure_rand(); + doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE); } + ////////////////////////////////////////////////////////////////////////////// // // dispatching functions @@ -129,9 +140,10 @@ namespace { // These functions dispatch to one or all registered wallets namespace { + struct CMainSignals { - // Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in. - boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction; + // Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. + boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction; // Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). boost::signals2::signal<void (const uint256 &)> EraseTransaction; // Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). @@ -143,10 +155,12 @@ struct CMainSignals { // Tells listeners to broadcast their data. boost::signals2::signal<void ()> Broadcast; } g_signals; -} + +} // anon namespace + void RegisterWallet(CWalletInterface* pwalletIn) { - g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); @@ -160,7 +174,7 @@ void UnregisterWallet(CWalletInterface* pwalletIn) { g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); } void UnregisterAllWallets() { @@ -172,8 +186,8 @@ void UnregisterAllWallets() { g_signals.SyncTransaction.disconnect_all_slots(); } -void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) { - g_signals.SyncTransaction(hash, tx, pblock); +void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) { + g_signals.SyncTransaction(tx, pblock); } ////////////////////////////////////////////////////////////////////////////// @@ -202,6 +216,10 @@ struct CNodeState { std::string name; // List of asynchronously-determined block rejections to notify this peer about. std::vector<CBlockReject> rejects; + // The best known block we know this peer has announced. + CBlockIndex *pindexBestKnownBlock; + // The hash of the last unknown block this peer has announced. + uint256 hashLastUnknownBlock; list<QueuedBlock> vBlocksInFlight; int nBlocksInFlight; list<uint256> vBlocksToDownload; @@ -212,6 +230,8 @@ struct CNodeState { CNodeState() { nMisbehavior = 0; fShouldBan = false; + pindexBestKnownBlock = NULL; + hashLastUnknownBlock = uint256(0); nBlocksToDownload = 0; nBlocksInFlight = 0; nLastBlockReceive = 0; @@ -273,7 +293,6 @@ void MarkBlockAsReceived(const uint256 &hash, NodeId nodeFrom = -1) { state->nLastBlockReceive = GetTimeMicros(); mapBlocksInFlight.erase(itInFlight); } - } // Requires cs_main. @@ -309,14 +328,48 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256 &hash) { mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } +/** Check whether the last unknown block a peer advertized is not yet known. */ +void ProcessBlockAvailability(NodeId nodeid) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + if (state->hashLastUnknownBlock != 0) { + map<uint256, CBlockIndex*>::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); + if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { + if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = itOld->second; + state->hashLastUnknownBlock = uint256(0); + } + } +} + +/** Update tracking information about which blocks a peer is assumed to have. */ +void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + ProcessBlockAvailability(nodeid); + + map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { + // An actually better block was announced. + if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = it->second; + } else { + // An unknown block was announced; just assume that the latest one is the best one. + state->hashLastUnknownBlock = hash; + } } +} // anon namespace + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); CNodeState *state = State(nodeid); if (state == NULL) return false; stats.nMisbehavior = state->nMisbehavior; + stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; return true; } @@ -370,12 +423,13 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { break; // Exponentially larger steps back, plus the genesis block. int nHeight = std::max(pindex->nHeight - nStep, 0); - // In case pindex is not in this chain, iterate pindex->pprev to find blocks. - while (pindex->nHeight > nHeight && !Contains(pindex)) - pindex = pindex->pprev; - // If pindex is in this chain, use direct height-based access. - if (pindex->nHeight > nHeight) + if (Contains(pindex)) { + // Use O(1) CChain index if possible. pindex = (*this)[nHeight]; + } else { + // Otherwise, use O(log n) skiplist. + pindex = pindex->GetAncestor(nHeight); + } if (vHave.size() > 10) nStep *= 2; } @@ -398,6 +452,8 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { } CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const { + if (pindex->nHeight > Height()) + pindex = pindex->GetAncestor(Height()); while (pindex && !Contains(pindex)) pindex = pindex->pprev; return pindex; @@ -487,7 +543,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) // Treat non-final transactions as non-standard to prevent a specific type // of double-spend attack, as well as DoS attacks. (if the transaction // can't be mined, the attacker isn't expending resources broadcasting it) - // Basically we don't want to propagate transactions that can't included in + // Basically we don't want to propagate transactions that can't be included in // the next block. // // However, IsFinalTx() is confusing... Without arguments, it uses @@ -520,7 +576,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed // keys. (remember the 520 byte limit on redeemScript size) That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 // bytes of scriptSig, which we round off to 1650 bytes for some minor // future-proofing. That's also enough to spend a 20-of-20 // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not @@ -548,7 +604,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) } if (whichType == TX_NULL_DATA) nDataOut++; - else if (txout.IsDust(CTransaction::minRelayTxFee)) { + else if (txout.IsDust(::minRelayTxFee)) { reason = "dust"; return false; } @@ -582,15 +638,13 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) } // -// Check transaction inputs, and make sure any -// pay-to-script-hash transactions are evaluating IsStandard scripts +// Check transaction inputs to mitigate two +// potential denial-of-service attacks: // -// Why bother? To avoid denial-of-service attacks; an attacker -// can submit a standard HASH... OP_EQUAL transaction, -// which will get accepted into blocks. The redemption -// script can be anything; an attacker could use a very -// expensive-to-check-upon-redemption script like: -// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// 1. scriptSigs with extra data stuffed into them, +// not consumed by scriptPubKey (or P2SH script) +// 2. P2SH scripts with a crazy number of expensive +// CHECKSIG/CHECKMULTISIG operations // bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) { @@ -614,8 +668,9 @@ bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations - // beside "push data" in the scriptSig the - // IsStandard() call returns false + // beside "push data" in the scriptSig + // IsStandard() will have already returned false + // and this method isn't called. vector<vector<unsigned char> > stack; if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, false, 0)) return false; @@ -627,16 +682,20 @@ bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) CScript subscript(stack.back().begin(), stack.back().end()); vector<vector<unsigned char> > vSolutions2; txnouttype whichType2; - if (!Solver(subscript, whichType2, vSolutions2)) - return false; - if (whichType2 == TX_SCRIPTHASH) - return false; - - int tmpExpected; - tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; + if (Solver(subscript, whichType2, vSolutions2)) + { + int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + else + { + // Any other Script with less than 15 sigops OK: + unsigned int sigops = subscript.GetSigOpCount(true); + // ... extra data left on the stack after execution is OK, too: + return (sigops <= MAX_P2SH_SIGOPS); + } } if (stack.size() != (unsigned int)nArgsExpected) @@ -786,12 +845,19 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } -int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode) +int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) { - // Base fee is either minTxFee or minRelayTxFee - CFeeRate baseFeeRate = (mode == GMF_RELAY) ? tx.minRelayTxFee : tx.minTxFee; + { + LOCK(mempool.cs); + uint256 hash = tx.GetHash(); + double dPriorityDelta = 0; + int64_t nFeeDelta = 0; + mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); + if (dPriorityDelta > 0 || nFeeDelta > 0) + return 0; + } - int64_t nMinFee = baseFeeRate.GetFee(nBytes); + int64_t nMinFee = ::minRelayTxFee.GetFee(nBytes); if (fAllowFree) { @@ -799,9 +865,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 // to be considered to fall into this category. We don't want to encourage sending // multiple transactions instead of one big transaction to avoid fees. - // * If we are creating a transaction we allow transactions up to 1,000 bytes - // to be considered safe and assume they can likely make it into this section. - if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) nMinFee = 0; } @@ -810,6 +874,60 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, return nMinFee; } +// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute. +bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize) +{ + static CCriticalSection csLimiter; + int64_t nNow = GetTime(); + + LOCK(csLimiter); + + dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + if (dCount >= nLimit*10*1000) + return true; + dCount += nSize; + return false; +} + +static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter) +{ + // Relaying double-spend attempts to our peers lets them detect when + // somebody might be trying to cheat them. However, blindly relaying + // every double-spend across the entire network gives attackers + // a denial-of-service attack: just generate a stream of double-spends + // re-spending the same (limited) set of outpoints owned by the attacker. + // So, we use a bloom filter and only relay (at most) the first double + // spend for each outpoint. False-positives ("we have already relayed") + // are OK, because if the peer doesn't hear about the double-spend + // from us they are very likely to hear about it from another peer, since + // each peer uses a different, randomized bloom filter. + + if (fInBlock || filter.contains(outPoint)) return false; + + // Apply an independent rate limit to double-spend relays + static double dRespendCount; + static int64_t nLastRespendTime; + static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100); + unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION); + + if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize)) + { + LogPrint("mempool", "Double-spend relay rejected by rate limiter\n"); + return false; + } + + LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize); + + // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM + // insertions + if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0) + filter.clear(); + + filter.insert(outPoint); + + return true; +} bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) @@ -839,15 +957,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions + bool relayableRespend = false; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) + // Does tx conflict with a member of the pool, and is it not equivalent to that member? + if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx)) { - // Disable replacement feature for now - return false; + relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter); + if (!relayableRespend) + return false; } } } @@ -907,7 +1028,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block - int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); + int64_t 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), @@ -916,31 +1037,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Continuously rate-limit free (really, very-low-fee)transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < CTransaction::minRelayTxFee.GetFee(nSize)) + if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) { - static CCriticalSection csFreeLimiter; static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); + static int64_t nLastFreeTime; + static int64_t nFreeLimit = GetArg("-limitfreerelay", 15); - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -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) + if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize)) return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "insufficient priority"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; } - if (fRejectInsaneFee && nFees > CTransaction::minRelayTxFee.GetFee(nSize) * 10000) + if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) return error("AcceptToMemoryPool: : insane fees %s, %d > %d", hash.ToString(), - nFees, CTransaction::minRelayTxFee.GetFee(nSize) * 10000); + nFees, ::minRelayTxFee.GetFee(nSize) * 10000); // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. @@ -948,13 +1061,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } - // Store transaction in memory - pool.addUnchecked(hash, entry); + + if (relayableRespend) + { + RelayTransaction(tx); + } + else + { + // Store transaction in memory + pool.addUnchecked(hash, entry); + } } - g_signals.SyncTransaction(hash, tx, NULL); + g_signals.SyncTransaction(tx, NULL); - return true; + return !relayableRespend; } @@ -1002,10 +1123,10 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree) +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee); } @@ -1194,118 +1315,6 @@ int64_t GetBlockValue(int nHeight, int64_t nFees) return nSubsidy + nFees; } -static const int64_t nTargetTimespan = 14 * 24 * 60 * 60; // two weeks -static const int64_t nTargetSpacing = 10 * 60; -static const int64_t nInterval = nTargetTimespan / nTargetSpacing; - -// -// minimum amount of work that could possibly be required nTime after -// minimum work required was nBase -// -unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime) -{ - const uint256 &bnLimit = Params().ProofOfWorkLimit(); - // Testnet has min-difficulty blocks - // after nTargetSpacing*2 time between blocks: - if (Params().AllowMinDifficultyBlocks() && nTime > nTargetSpacing*2) - return bnLimit.GetCompact(); - - uint256 bnResult; - bnResult.SetCompact(nBase); - while (nTime > 0 && bnResult < bnLimit) - { - // Maximum 400% adjustment... - bnResult *= 4; - // ... in best-case exactly 4-times-normal target time - nTime -= nTargetTimespan*4; - } - if (bnResult > bnLimit) - bnResult = bnLimit; - return bnResult.GetCompact(); -} - -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) -{ - unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact(); - - // Genesis block - if (pindexLast == NULL) - return nProofOfWorkLimit; - - // Only change once per interval - if ((pindexLast->nHeight+1) % nInterval != 0) - { - if (Params().AllowMinDifficultyBlocks()) - { - // Special difficulty rule for testnet: - // If the new block's timestamp is more than 2* 10 minutes - // then allow mining of a min-difficulty block. - if (pblock->nTime > pindexLast->nTime + nTargetSpacing*2) - return nProofOfWorkLimit; - else - { - // Return the last non-special-min-difficulty-rules-block - const CBlockIndex* pindex = pindexLast; - while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit) - pindex = pindex->pprev; - return pindex->nBits; - } - } - return pindexLast->nBits; - } - - // Go back by what we want to be 14 days worth of blocks - const CBlockIndex* pindexFirst = pindexLast; - for (int i = 0; pindexFirst && i < nInterval-1; i++) - pindexFirst = pindexFirst->pprev; - assert(pindexFirst); - - // Limit adjustment step - int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); - if (nActualTimespan < nTargetTimespan/4) - nActualTimespan = nTargetTimespan/4; - if (nActualTimespan > nTargetTimespan*4) - nActualTimespan = nTargetTimespan*4; - - // Retarget - uint256 bnNew; - uint256 bnOld; - bnNew.SetCompact(pindexLast->nBits); - bnOld = bnNew; - bnNew *= nActualTimespan; - bnNew /= nTargetTimespan; - - if (bnNew > Params().ProofOfWorkLimit()) - bnNew = Params().ProofOfWorkLimit(); - - /// debug print - LogPrintf("GetNextWorkRequired RETARGET\n"); - LogPrintf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan); - LogPrintf("Before: %08x %s\n", pindexLast->nBits, bnOld.ToString()); - LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString()); - - return bnNew.GetCompact(); -} - -bool CheckProofOfWork(uint256 hash, unsigned int nBits) -{ - bool fNegative; - bool fOverflow; - uint256 bnTarget; - bnTarget.SetCompact(nBits, &fNegative, &fOverflow); - - // Check range - if (fNegative || bnTarget == 0 || fOverflow || bnTarget > Params().ProofOfWorkLimit()) - return error("CheckProofOfWork() : nBits below minimum work"); - - // Check proof of work matches claimed amount - if (hash > bnTarget) - return error("CheckProofOfWork() : hash doesn't match nBits"); - - return true; -} - bool IsInitialBlockDownload() { LOCK(cs_main); @@ -1479,7 +1488,7 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) { bool ret; // mark inputs spent @@ -1494,7 +1503,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach } // add outputs - ret = inputs.SetCoins(txhash, CCoins(tx, nHeight)); + ret = inputs.SetCoins(tx.GetHash(), CCoins(tx, nHeight)); assert(ret); } @@ -1767,8 +1776,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { - for (unsigned int i = 0; i < block.vtx.size(); i++) { - uint256 hash = block.GetTxHash(i); + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + const uint256& hash = tx.GetHash(); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); @@ -1777,7 +1786,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // BIP16 didn't become active until Apr 1 2012 int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); + bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); unsigned int flags = SCRIPT_VERIFY_NOCACHE | (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); @@ -1829,11 +1838,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } CTxUndo txundo; - UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); - vPos.push_back(std::make_pair(block.GetTxHash(i), pos)); + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime = GetTimeMicros() - nStart; @@ -1892,13 +1901,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C assert(ret); // Watch for transactions paying to me - for (unsigned int i = 0; i < block.vtx.size(); i++) - g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); + BOOST_FOREACH(const CTransaction& tx, block.vtx) + g_signals.SyncTransaction(tx, &block); // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; g_signals.UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = block.GetTxHash(0); + hashPrevBestCoinBase = block.vtx[0].GetHash(); return true; } @@ -1996,7 +2005,7 @@ bool static DisconnectTip(CValidationState &state) { // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx.GetHash(), tx, NULL); + SyncWithWallets(tx, NULL); } return true; } @@ -2036,11 +2045,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx.GetHash(), tx, NULL); + SyncWithWallets(tx, NULL); } // ... and about transactions that got confirmed: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx.GetHash(), tx, &block); + SyncWithWallets(tx, &block); } return true; } @@ -2206,6 +2215,7 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + pindexNew->BuildSkip(); } pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork(); pindexNew->RaiseValidity(BLOCK_VALID_TREE); @@ -2351,28 +2361,6 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"), REJECT_INVALID, "time-too-new"); - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) - { - // Extra checks to prevent "fill up memory by spamming with bogus blocks" - int64_t deltaTime = block.GetBlockTime() - pcheckpoint->nTime; - if (deltaTime < 0) - { - return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"), - REJECT_CHECKPOINT, "time-too-old"); - } - bool fOverflow = false; - uint256 bnNewBlock; - bnNewBlock.SetCompact(block.nBits, NULL, &fOverflow); - uint256 bnRequired; - bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); - if (fOverflow || bnNewBlock > bnRequired) - { - return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"), - REJECT_INVALID, "bad-diffbits"); - } - } - return true; } @@ -2403,16 +2391,11 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); - // Build the merkle tree already. We need it anyway later, and it makes the - // block cache the transaction hashes, which means they don't need to be - // recalculated many times during this block's validation. - block.BuildMerkleTree(); - // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set<uint256> uniqueTx; - for (unsigned int i = 0; i < block.vtx.size(); i++) { - uniqueTx.insert(block.GetTxHash(i)); + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + uniqueTx.insert(tx.GetHash()); } if (uniqueTx.size() != block.vtx.size()) return state.DoS(100, error("CheckBlock() : duplicate transaction"), @@ -2428,7 +2411,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo REJECT_INVALID, "bad-blk-sigops", true); // Check merkle root - if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back()) + if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree()) return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); @@ -2448,6 +2431,28 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex return state.Invalid(error("AcceptBlock() : block is marked invalid"), 0, "duplicate"); } + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64_t deltaTime = block.GetBlockTime() - pcheckpoint->GetBlockTime(); + if (deltaTime < 0) + { + return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"), + REJECT_CHECKPOINT, "time-too-old"); + } + bool fOverflow = false; + uint256 bnNewBlock; + bnNewBlock.SetCompact(block.nBits, NULL, &fOverflow); + uint256 bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (fOverflow || bnNewBlock > bnRequired) + { + return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"), + REJECT_INVALID, "bad-diffbits"); + } + } + // Get prev block index CBlockIndex* pindexPrev = NULL; int nHeight = 0; @@ -2541,7 +2546,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos blockPos; if (dbp != NULL) blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL)) + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) return error("AcceptBlock() : FindBlockPos failed"); if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos)) @@ -2568,6 +2573,55 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ +int static inline InvertLowestOne(int n) { return n & (n - 1); } + +/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */ +int static inline GetSkipHeight(int height) { + if (height < 2) + return 0; + + // Determine which height to jump back to. Any number strictly lower than height is acceptable, + // but the following expression seems to perform well in simulations (max 110 steps to go back + // up to 2**18 blocks). + return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height); +} + +CBlockIndex* CBlockIndex::GetAncestor(int height) +{ + if (height > nHeight || height < 0) + return NULL; + + CBlockIndex* pindexWalk = this; + int heightWalk = nHeight; + while (heightWalk > height) { + int heightSkip = GetSkipHeight(heightWalk); + int heightSkipPrev = GetSkipHeight(heightWalk - 1); + if (heightSkip == height || + (heightSkip > height && !(heightSkipPrev < heightSkip - 2 && + heightSkipPrev >= height))) { + // Only follow pskip if pprev->pskip isn't better than pskip->pprev. + pindexWalk = pindexWalk->pskip; + heightWalk = heightSkip; + } else { + pindexWalk = pindexWalk->pprev; + heightWalk--; + } + } + return pindexWalk; +} + +const CBlockIndex* CBlockIndex::GetAncestor(int height) const +{ + return const_cast<CBlockIndex*>(this)->GetAncestor(height); +} + +void CBlockIndex::BuildSkip() +{ + if (pprev) + pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { AssertLockHeld(cs_main); @@ -2682,8 +2736,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) for (unsigned int i = 0; i < block.vtx.size(); i++) { - uint256 hash = block.vtx[i].GetHash(); - if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) + const uint256& hash = block.vtx[i].GetHash(); + if (filter.IsRelevantAndUpdate(block.vtx[i])) { vMatch.push_back(true); vMatchedTxn.push_back(make_pair(i, hash)); @@ -2918,6 +2972,8 @@ bool static LoadBlockIndexDB() setBlockIndexValid.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindex; + if (pindex->pprev) + pindex->BuildSkip(); } // Load block file info @@ -3088,7 +3144,7 @@ bool InitBlockIndex() { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) return error("LoadBlockIndex() : FindBlockPos failed"); if (!WriteBlockToDisk(block, blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); @@ -3477,10 +3533,10 @@ void static ProcessGetData(CNode* pfrom) } } -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); - LogPrint("net", "received: %s (%u bytes)\n", strCommand, vRecv.size()); + LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -3512,7 +3568,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version - LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion); + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; @@ -3593,7 +3649,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString()); + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id); AddTimeData(pfrom->addr, nTime); } @@ -3700,7 +3756,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); - LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); + LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); if (!fAlreadyHave) { if (!fImporting && !fReindex) { @@ -3713,6 +3769,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); } + if (inv.type == MSG_BLOCK) + UpdateBlockAvailability(pfrom->GetId(), inv.hash); + // Track requests for our stuff g_signals.Inventory(inv.hash); } @@ -3730,10 +3789,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } if (fDebug || (vInv.size() != 1)) - LogPrint("net", "received getdata (%u invsz)\n", vInv.size()); + LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) - LogPrint("net", "received getdata for: %s\n", vInv[0].ToString()); + LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); @@ -3755,7 +3814,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; - LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), nLimit); + LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) @@ -3832,14 +3891,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); - RelayTransaction(tx, inv.hash); + RelayTransaction(tx); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); - LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %u)\n", - pfrom->addr.ToString(), pfrom->cleanSubVer, + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n", + pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), mempool.mapTx.size()); @@ -3862,7 +3921,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx, orphanHash); + RelayTransaction(orphanTx); mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); @@ -3892,8 +3951,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), - pfrom->addr.ToString(), pfrom->cleanSubVer, + LogPrint("mempool", "%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(), inv.hash); @@ -3908,7 +3967,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CBlock block; vRecv >> block; - LogPrint("net", "received block %s\n", block.GetHash().ToString()); + LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id); // block.print(); CInv inv(MSG_BLOCK, block.GetHash()); @@ -3923,6 +3982,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; ProcessBlock(state, pfrom, &block); + int nDoS; + if (state.IsInvalid(nDoS)) { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason(), inv.hash); + if (nDoS > 0) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), nDoS); + } + } + } @@ -3947,7 +4016,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CTransaction tx; bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx, hash)) || + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { @@ -3984,7 +4053,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "pong") { - int64_t pingUsecEnd = GetTimeMicros(); + int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; @@ -4025,8 +4094,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } if (!(sProblem.empty())) { - LogPrint("net", "pong %s %s: %s, %x expected, %x received, %u bytes\n", - pfrom->addr.ToString(), + LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", + pfrom->id, pfrom->cleanSubVer, sProblem, pfrom->nPingNonceSent, @@ -4144,6 +4213,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else { // Ignore unknown commands for extensibility + LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } @@ -4234,7 +4304,7 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - fRet = ProcessMessage(pfrom, strCommand, vRecv); + fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); boost::this_thread::interruption_point(); } catch (std::ios_base::failure& e) @@ -4265,7 +4335,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand, nMessageSize); + LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id); break; } @@ -4458,6 +4528,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->fDisconnect = true; } + // Update knowledge of peer's block availability. + ProcessBlockAvailability(pto->GetId()); + // // Message: getdata (blocks) // @@ -4466,7 +4539,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) uint256 hash = state.vBlocksToDownload.front(); vGetData.push_back(CInv(MSG_BLOCK, hash)); MarkBlockAsInFlight(pto->GetId(), hash); - LogPrint("net", "Requesting block %s from %s\n", hash.ToString(), state.name); + LogPrint("net", "Requesting block %s peer=%d\n", hash.ToString(), pto->id); if (vGetData.size() >= 1000) { pto->PushMessage("getdata", vGetData); @@ -4483,7 +4556,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!AlreadyHave(inv)) { if (fDebug) - LogPrint("net", "sending getdata: %s\n", inv.ToString()); + LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); vGetData.push_back(inv); if (vGetData.size() >= 1000) { diff --git a/src/main.h b/src/main.h index 5a0aedcde1..f6bac889be 100644 --- a/src/main.h +++ b/src/main.h @@ -7,7 +7,7 @@ #define BITCOIN_MAIN_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "chainparams.h" @@ -43,6 +43,8 @@ static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000; static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +/** Maxiumum number of signature check operations in an IsStandard() P2SH script */ +static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of orphan transactions kept in memory */ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; /** Default for -maxorphanblocks, maximum number of orphan blocks kept in memory */ @@ -91,6 +93,7 @@ extern bool fBenchmark; extern int nScriptCheckThreads; extern bool fTxIndex; extern unsigned int nCoinCacheSize; +extern CFeeRate minRelayTxFee; // Minimum disk space required - used in CheckDiskSpace() static const uint64_t nMinDiskSpace = 52428800; @@ -106,6 +109,9 @@ struct CNodeStateStats; struct CBlockTemplate; +/** Initialize respend bloom filter **/ +void InitRespendFilter(); + /** Register a wallet to receive updates from core */ void RegisterWallet(CWalletInterface* pwalletIn); /** Unregister a wallet from core */ @@ -113,7 +119,7 @@ void UnregisterWallet(CWalletInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllWallets(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL); +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL); /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); @@ -146,10 +152,6 @@ bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); -/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ -bool CheckProofOfWork(uint256 hash, unsigned int nBits); -/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ -unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core */ @@ -159,7 +161,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState &state); int64_t GetBlockValue(int nHeight, int64_t nFees); -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock); void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev); @@ -188,6 +189,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa struct CNodeStateStats { int nMisbehavior; + int nSyncHeight; }; struct CDiskBlockPos @@ -244,14 +246,7 @@ struct CDiskTxPos : public CDiskBlockPos }; - -enum GetMinFee_mode -{ - GMF_RELAY, - GMF_SEND, -}; - -int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode); +int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); // // Check transaction inputs, and make sure any @@ -294,7 +289,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach std::vector<CScriptCheck> *pvChecks = NULL); // Apply the effects of this transaction on the UTXO set represented by view -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash); +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight); // Context-independent validity checks bool CheckTransaction(const CTransaction& tx, CValidationState& state); @@ -458,7 +453,7 @@ public: int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(bool fLimitFree=true); + bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true); }; @@ -679,6 +674,9 @@ public: // pointer to the index of the predecessor of this block CBlockIndex* pprev; + // pointer to the index of some further predecessor of this block + CBlockIndex* pskip; + // height of the entry in the chain. The genesis block has height 0 int nHeight; @@ -714,10 +712,11 @@ public: // (memory only) Sequencial id assigned to distinguish order in which blocks are received. uint32_t nSequenceId; - CBlockIndex() + void SetNull() { phashBlock = NULL; pprev = NULL; + pskip = NULL; nHeight = 0; nFile = 0; nDataPos = 0; @@ -735,19 +734,14 @@ public: nNonce = 0; } + CBlockIndex() + { + SetNull(); + } + CBlockIndex(CBlockHeader& block) { - phashBlock = NULL; - pprev = NULL; - nHeight = 0; - nFile = 0; - nDataPos = 0; - nUndoPos = 0; - nChainWork = 0; - nTx = 0; - nChainTx = 0; - nStatus = 0; - nSequenceId = 0; + SetNull(); nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -812,11 +806,6 @@ public: return (~bnTarget / (bnTarget + 1)) + 1; } - bool CheckIndex() const - { - return CheckProofOfWork(GetBlockHash(), nBits); - } - enum { nMedianTimeSpan=11 }; int64_t GetMedianTimePast() const @@ -876,9 +865,14 @@ public: } return false; } -}; + // Build the skiplist pointer for this entry. + void BuildSkip(); + // Efficiently find an ancestor of this block. + CBlockIndex* GetAncestor(int height); + const CBlockIndex* GetAncestor(int height) const; +}; /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex @@ -1129,7 +1123,7 @@ public: class CWalletInterface { protected: - virtual void SyncTransaction(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) =0; + virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) =0; virtual void EraseFromWallet(const uint256 &hash) =0; virtual void SetBestChain(const CBlockLocator &locator) =0; virtual void UpdatedTransaction(const uint256 &hash) =0; diff --git a/src/miner.cpp b/src/miner.cpp index a0a728fb81..17918a1280 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -3,55 +3,26 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <inttypes.h> + #include "miner.h" #include "core.h" +#include "hash.h" #include "main.h" #include "net.h" +#include "pow.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif + +using namespace std; + ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner // -int static FormatHashBlocks(void* pbuffer, unsigned int len) -{ - unsigned char* pdata = (unsigned char*)pbuffer; - unsigned int blocks = 1 + ((len + 8) / 64); - unsigned char* pend = pdata + 64 * blocks; - memset(pdata + len, 0, 64 * blocks - len); - pdata[len] = 0x80; - unsigned int bits = len * 8; - pend[-1] = (bits >> 0) & 0xff; - pend[-2] = (bits >> 8) & 0xff; - pend[-3] = (bits >> 16) & 0xff; - pend[-4] = (bits >> 24) & 0xff; - return blocks; -} - -static const unsigned int pSHA256InitState[8] = -{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -void SHA256Transform(void* pstate, void* pinput, const void* pinit) -{ - SHA256_CTX ctx; - unsigned char data[64]; - - SHA256_Init(&ctx); - - for (int i = 0; i < 16; i++) - ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); - - for (int i = 0; i < 8; i++) - ctx.h[i] = ((uint32_t*)pinit)[i]; - - SHA256_Update(&ctx, data, sizeof(data)); - for (int i = 0; i < 8; i++) - ((uint32_t*)pstate)[i] = ctx.h[i]; -} - // // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the @@ -81,7 +52,6 @@ public: } }; - uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; @@ -90,8 +60,10 @@ typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority; class TxPriorityCompare { bool byFee; + public: TxPriorityCompare(bool _byFee) : byFee(_byFee) { } + bool operator()(const TxPriority& a, const TxPriority& b) { if (byFee) @@ -118,14 +90,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) CBlock *pblock = &pblocktemplate->block; // pointer for convenience // Create coinbase tx - CTransaction txNew; + CMutableTransaction txNew; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); txNew.vout[0].scriptPubKey = scriptPubKeyIn; - // Add our coinbase tx as first transaction - pblock->vtx.push_back(txNew); + // Add dummy coinbase tx as first transaction + pblock->vtx.push_back(CTransaction()); pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOps.push_back(-1); // updated at end @@ -146,6 +118,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Collect memory pool transactions into the block int64_t nFees = 0; + { LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); @@ -215,6 +188,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); dPriority = tx.ComputePriority(dPriority, nTxSize); + uint256 hash = tx.GetHash(); + mempool.ApplyDeltas(hash, dPriority, nTotalIn); + CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize); if (porphan) @@ -256,10 +232,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) continue; // Skip free transactions if we're past the minimum block size: - if (fSortedByFee && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) + const uint256& hash = tx.GetHash(); + double dPriorityDelta = 0; + int64_t nFeeDelta = 0; + mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); + if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) continue; - // Prioritize by fee once past the priority size or we run out of high-priority + // Prioritise by fee once past the priority size or we run out of high-priority // transactions: if (!fSortedByFee && ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority))) @@ -286,8 +266,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) continue; CTxUndo txundo; - uint256 hash = tx.GetHash(); - UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash); + UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1); // Added pblock->vtx.push_back(tx); @@ -301,7 +280,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (fPrintPriority) { LogPrintf("priority %.1f fee %s txid %s\n", - dPriority, feeRate.ToString(), tx.GetHash().ToString()); + dPriority, feeRate.ToString(), tx.GetHash().ToString()); } // Add transactions that depend on this one to the priority queue @@ -326,7 +305,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockSize = nBlockSize; LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); - pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + // Compute final coinbase transaction. + txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + txNew.vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; // Fill in header @@ -334,7 +316,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) UpdateTime(*pblock, pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; - pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CBlockIndex indexDummy(*pblock); @@ -360,58 +341,14 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 - pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; - assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + CMutableTransaction txCoinbase(pblock->vtx[0]); + txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; + assert(txCoinbase.vin[0].scriptSig.size() <= 100); + pblock->vtx[0] = txCoinbase; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } - -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) -{ - // - // Pre-build hash buffers - // - struct - { - struct unnamed2 - { - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - } - block; - unsigned char pchPadding0[64]; - uint256 hash1; - unsigned char pchPadding1[64]; - } - tmp; - memset(&tmp, 0, sizeof(tmp)); - - tmp.block.nVersion = pblock->nVersion; - tmp.block.hashPrevBlock = pblock->hashPrevBlock; - tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; - tmp.block.nTime = pblock->nTime; - tmp.block.nBits = pblock->nBits; - tmp.block.nNonce = pblock->nNonce; - - FormatHashBlocks(&tmp.block, sizeof(tmp.block)); - FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); - - // Byte swap all the input buffer - for (unsigned int i = 0; i < sizeof(tmp)/4; i++) - ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); - - // Precalc the first half of the first hash, which stays constant - SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); - - memcpy(pdata, &tmp.block, 128); - memcpy(phash1, &tmp.hash1, 64); -} - #ifdef ENABLE_WALLET ////////////////////////////////////////////////////////////////////////////// // @@ -422,34 +359,34 @@ int64_t nHPSTimerStart = 0; // // ScanHash scans nonces looking for a hash with at least some zero bits. -// It operates on big endian data. Caller does the byte reversing. -// All input buffers are 16-byte aligned. nNonce is usually preserved -// between calls, but periodically or if nNonce is 0xffff0000 or above, -// the block is rebuilt and nNonce starts over at zero. +// The nonce is usually preserved between calls, but periodically or if the +// nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at +// zero. // -unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash) { - unsigned int& nNonce = *(unsigned int*)(pdata + 12); - for (;;) - { - // Crypto++ SHA256 - // Hash pdata using pmidstate as the starting state into - // pre-formatted buffer phash1, then hash phash1 into phash + // Write the first 76 bytes of the block header to a double-SHA256 state. + CHash256 hasher; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << *pblock; + assert(ss.size() == 80); + hasher.Write((unsigned char*)&ss[0], 76); + + while (true) { nNonce++; - SHA256Transform(phash1, pdata, pmidstate); - SHA256Transform(phash, phash1, pSHA256InitState); + + // Write the last 4 bytes of the block header (the nonce) to a copy of + // the double-SHA256 state, and compute the result. + CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash); // Return the nonce if the hash has at least some zero bits, // caller will check if it has enough to reach the target - if (((unsigned short*)phash)[14] == 0) - return nNonce; + if (((uint16_t*)phash)[15] == 0) + return true; // If nothing found after trying for a while, return -1 if ((nNonce & 0xffff) == 0) - { - nHashesDone = 0xffff+1; - return (unsigned int) -1; - } + return false; if ((nNonce & 0xfff) == 0) boost::this_thread::interruption_point(); } @@ -513,135 +450,115 @@ void static BitcoinMiner(CWallet *pwallet) CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - try { while (true) { - if (Params().MiningRequiresPeers()) { - // Busy-wait for the network to come online so we don't waste time mining - // on an obsolete chain. In regtest mode we expect to fly solo. - while (vNodes.empty()) - MilliSleep(1000); - } - - // - // Create new block - // - unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrev = chainActive.Tip(); - - auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); - if (!pblocktemplate.get()) - return; - CBlock *pblock = &pblocktemplate->block; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - - LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - - // - // Pre-build hash buffers - // - char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); - char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); - char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); - - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); - unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); - unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); - - - // - // Search - // - int64_t nStart = GetTime(); - uint256 hashTarget = uint256().SetCompact(pblock->nBits); - uint256 hashbuf[2]; - uint256& hash = *alignup<16>(hashbuf); - while (true) - { - unsigned int nHashesDone = 0; - unsigned int nNonceFound; - - // Crypto++ SHA256 - nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1, - (char*)&hash, nHashesDone); - - // Check if something found - if (nNonceFound != (unsigned int) -1) - { - for (unsigned int i = 0; i < sizeof(hash)/4; i++) - ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); + try { + while (true) { + if (Params().MiningRequiresPeers()) { + // Busy-wait for the network to come online so we don't waste time mining + // on an obsolete chain. In regtest mode we expect to fly solo. + while (vNodes.empty()) + MilliSleep(1000); + } - if (hash <= hashTarget) + // + // Create new block + // + unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrev = chainActive.Tip(); + + auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); + if (!pblocktemplate.get()) + return; + CBlock *pblock = &pblocktemplate->block; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), + ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); + + // + // Search + // + int64_t nStart = GetTime(); + uint256 hashTarget = uint256().SetCompact(pblock->nBits); + uint256 hash; + uint32_t nNonce = 0; + uint32_t nOldNonce = 0; + while (true) { + bool fFound = ScanHash(pblock, nNonce, &hash); + uint32_t nHashesDone = nNonce - nOldNonce; + nOldNonce = nNonce; + + // Check if something found + if (fFound) { - // Found a solution - pblock->nNonce = ByteReverse(nNonceFound); - assert(hash == pblock->GetHash()); + if (hash <= hashTarget) + { + // Found a solution + pblock->nNonce = nNonce; + assert(hash == pblock->GetHash()); - SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock, *pwallet, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); + SetThreadPriority(THREAD_PRIORITY_NORMAL); + CheckWork(pblock, *pwallet, reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); - // In regression test mode, stop mining after a block is found. - if (Params().MineBlocksOnDemand()) - throw boost::thread_interrupted(); + // In regression test mode, stop mining after a block is found. + if (Params().MineBlocksOnDemand()) + throw boost::thread_interrupted(); - break; + break; + } } - } - // Meter hashes/sec - static int64_t nHashCounter; - if (nHPSTimerStart == 0) - { - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - } - else - nHashCounter += nHashesDone; - if (GetTimeMillis() - nHPSTimerStart > 4000) - { - static CCriticalSection cs; + // Meter hashes/sec + static int64_t nHashCounter; + if (nHPSTimerStart == 0) + { + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + } + else + nHashCounter += nHashesDone; + if (GetTimeMillis() - nHPSTimerStart > 4000) { - LOCK(cs); - if (GetTimeMillis() - nHPSTimerStart > 4000) + static CCriticalSection cs; { - dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - static int64_t nLogTime; - if (GetTime() - nLogTime > 30 * 60) + LOCK(cs); + if (GetTimeMillis() - nHPSTimerStart > 4000) { - nLogTime = GetTime(); - LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); + dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); + nHPSTimerStart = GetTimeMillis(); + nHashCounter = 0; + static int64_t nLogTime; + if (GetTime() - nLogTime > 30 * 60) + { + nLogTime = GetTime(); + LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); + } } } } - } - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - // Regtest mode doesn't require peers - if (vNodes.empty() && Params().MiningRequiresPeers()) - break; - if (nBlockNonce >= 0xffff0000) - break; - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - break; - if (pindexPrev != chainActive.Tip()) - break; - - // Update nTime every few seconds - UpdateTime(*pblock, pindexPrev); - nBlockTime = ByteReverse(pblock->nTime); - if (Params().AllowMinDifficultyBlocks()) - { - // Changing pblock->nTime can change work required on testnet: - nBlockBits = ByteReverse(pblock->nBits); - hashTarget.SetCompact(pblock->nBits); + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // Regtest mode doesn't require peers + if (vNodes.empty() && Params().MiningRequiresPeers()) + break; + if (nNonce >= 0xffff0000) + break; + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (pindexPrev != chainActive.Tip()) + break; + + // Update nTime every few seconds + UpdateTime(*pblock, pindexPrev); + if (Params().AllowMinDifficultyBlocks()) + { + // Changing pblock->nTime can change work required on testnet: + hashTarget.SetCompact(pblock->nBits); + } } } - } } + } catch (boost::thread_interrupted) { LogPrintf("BitcoinMiner terminated\n"); @@ -676,4 +593,4 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } -#endif +#endif // ENABLE_WALLET diff --git a/src/miner.h b/src/miner.h index dcd61d8fd9..1fa499dc5b 100644 --- a/src/miner.h +++ b/src/miner.h @@ -23,12 +23,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey); /** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); -/** Do mining precalculation */ -void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); /** Check mined block */ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); -/** Base sha256 mining transform */ -void SHA256Transform(void* pstate, void* pinput, const void* pinit); extern double dHashesPerSec; extern int64_t nHPSTimerStart; diff --git a/src/net.cpp b/src/net.cpp index 757a06aaed..6a660dc9bd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "net.h" @@ -36,6 +36,17 @@ #define MSG_NOSIGNAL 0 #endif +// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h. +// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version. +#ifdef WIN32 +#ifndef PROTECTION_LEVEL_UNRESTRICTED +#define PROTECTION_LEVEL_UNRESTRICTED 10 +#endif +#ifndef IPV6_PROTECTION_LEVEL +#define IPV6_PROTECTION_LEVEL 23 +#endif +#endif + using namespace std; using namespace boost; @@ -480,8 +491,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) { addrman.Attempt(addrConnect); - LogPrint("net", "connected %s\n", pszDest ? pszDest : addrConnect.ToString()); - // Set to non-blocking #ifdef WIN32 u_long nOne = 1; @@ -502,6 +511,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) } pnode->nTimeConnected = GetTime(); + return pnode; } @@ -513,7 +523,7 @@ void CNode::CloseSocketDisconnect() fDisconnect = true; if (hSocket != INVALID_SOCKET) { - LogPrint("net", "disconnecting node %s\n", addrName); + LogPrint("net", "disconnecting peer=%d\n", id); closesocket(hSocket); hSocket = INVALID_SOCKET; } @@ -528,10 +538,6 @@ void CNode::CloseSocketDisconnect() pnodeSync = NULL; } -void CNode::Cleanup() -{ -} - void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); @@ -541,7 +547,10 @@ void CNode::PushVersion() CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString()); + if (fLogIPs) + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + 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); } @@ -646,6 +655,9 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) pch += handled; nBytes -= handled; + + if (msg.complete()) + msg.nTime = GetTimeMicros(); } return true; @@ -678,7 +690,6 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes) // switch state to reading message data in_data = true; - vRecv.resize(hdr.nMessageSize); return nCopy; } @@ -688,6 +699,11 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) unsigned int nRemaining = hdr.nMessageSize - nDataPos; unsigned int nCopy = std::min(nRemaining, nBytes); + if (vRecv.size() < nDataPos + nCopy) { + // Allocate up to 256 KiB ahead, but never more than the total message size. + vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); + } + memcpy(&vRecv[nDataPos], pch, nCopy); nDataPos += nCopy; @@ -773,7 +789,6 @@ void ThreadSocketHandler() // close socket and cleanup pnode->CloseSocketDisconnect(); - pnode->Cleanup(); // hold in disconnected pool until all refs are released if (pnode->fNetworkNode || pnode->fInbound) @@ -817,7 +832,6 @@ void ThreadSocketHandler() uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } - // // Find which sockets have data to receive // @@ -839,6 +853,7 @@ void ThreadSocketHandler() hSocketMax = max(hSocketMax, hListenSocket); have_fds = true; } + { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -899,58 +914,58 @@ void ThreadSocketHandler() MilliSleep(timeout.tv_usec/1000); } - // // Accept new connections // BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { - struct sockaddr_storage sockaddr; - socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); - CAddress addr; - int nInbound = 0; - - if (hSocket != INVALID_SOCKET) - if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) - LogPrintf("Warning: Unknown socket family\n"); - + if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->fInbound) - nInbound++; - } + struct sockaddr_storage sockaddr; + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr; + int nInbound = 0; + + if (hSocket != INVALID_SOCKET) + if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) + LogPrintf("Warning: Unknown socket family\n"); - if (hSocket == INVALID_SOCKET) - { - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK) - LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); - } - else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) - { - closesocket(hSocket); - } - else if (CNode::IsBanned(addr)) - { - LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); - closesocket(hSocket); - } - else - { - LogPrint("net", "accepted connection %s\n", addr.ToString()); - CNode* pnode = new CNode(hSocket, addr, "", true); - pnode->AddRef(); { LOCK(cs_vNodes); - vNodes.push_back(pnode); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->fInbound) + nInbound++; + } + + if (hSocket == INVALID_SOCKET) + { + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK) + LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); + } + else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) + { + closesocket(hSocket); + } + else if (CNode::IsBanned(addr)) + { + LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); + closesocket(hSocket); + } + else + { + CNode* pnode = new CNode(hSocket, addr, "", true); + pnode->AddRef(); + + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } } } } - // // Service each socket // @@ -1029,7 +1044,7 @@ void ThreadSocketHandler() { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { - LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) @@ -1588,18 +1603,16 @@ bool BindListenPort(const CService &addrBind, string& strError) return false; } +#ifndef WIN32 #ifdef SO_NOSIGPIPE // Different way of disabling SIGPIPE on BSD setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); #endif - -#ifndef WIN32 // Allow binding if the port is still in TIME_WAIT state after - // the program was closed and restarted. Not an issue on windows. + // the program was closed and restarted. Not an issue on windows! setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); #endif - #ifdef WIN32 // Set to non-blocking, incoming connections will also inherit this if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) @@ -1623,10 +1636,8 @@ bool BindListenPort(const CService &addrBind, string& strError) #endif #endif #ifdef WIN32 - int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; - int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; - // this call is allowed to fail - setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int)); + int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)); #endif } @@ -1789,6 +1800,7 @@ public: delete pnode; vNodes.clear(); vNodesDisconnected.clear(); + vhListenSocket.clear(); delete semOutbound; semOutbound = NULL; delete pnodeLocalHost; @@ -1808,17 +1820,17 @@ instance_of_cnetcleanup; -void RelayTransaction(const CTransaction& tx, const uint256& hash) +void RelayTransaction(const CTransaction& tx) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(10000); ss << tx; - RelayTransaction(tx, hash, ss); + RelayTransaction(tx, ss); } -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss) +void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { - CInv inv(MSG_TX, hash); + CInv inv(MSG_TX, tx.GetHash()); { LOCK(cs_mapRelay); // Expire old relay messages @@ -1840,7 +1852,7 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt LOCK(pnode->cs_filter); if (pnode->pfilter) { - if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) + if (pnode->pfilter->IsRelevantAndUpdate(tx)) pnode->PushInventory(inv); } else pnode->PushInventory(inv); @@ -28,14 +28,13 @@ #include <boost/signals2/signal.hpp> #include <openssl/rand.h> - class CAddrMan; class CBlockIndex; class CNode; namespace boost { class thread_group; -} +} // namespace boost /** Time between pings automatically sent out for latency probing and keepalive (in seconds). */ static const int PING_INTERVAL = 2 * 60; @@ -174,11 +173,14 @@ public: CDataStream vRecv; // received message data unsigned int nDataPos; + int64_t nTime; // time (in microseconds) of message receipt. + CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { hdrbuf.resize(24); in_data = false; nHdrPos = 0; nDataPos = 0; + nTime = 0; } bool complete() const @@ -331,6 +333,11 @@ public: id = nLastNodeId++; } + if (fLogIPs) + LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); + else + LogPrint("net", "Added connection peer=%d\n", id); + // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) PushVersion(); @@ -447,7 +454,7 @@ public: nRequestTime = it->second; else nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s)\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); + LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; @@ -515,7 +522,7 @@ public: assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); - LogPrint("net", "(%d bytes)\n", nSize); + LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); ssSend.GetAndClear(*it); @@ -693,8 +700,6 @@ public: void Subscribe(unsigned int nChannel, unsigned int nHops=0); void CancelSubscribe(unsigned int nChannel); void CloseSocketDisconnect(); - void Cleanup(); - // Denial-of-service detection/prevention // The idea is to detect peers that are behaving @@ -726,8 +731,8 @@ public: class CTransaction; -void RelayTransaction(const CTransaction& tx, const uint256& hash); -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); +void RelayTransaction(const CTransaction& tx); +void RelayTransaction(const CTransaction& tx, const CDataStream& ss); /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/netbase.cpp b/src/netbase.cpp index 4aa7367f39..067cfa024b 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -3,6 +3,14 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifdef HAVE_CONFIG_H +#include "bitcoin-config.h" +#endif + +#ifdef HAVE_GETADDRINFO_A +#include <netdb.h> +#endif + #include "netbase.h" #include "hash.h" @@ -11,6 +19,9 @@ #include "util.h" #ifndef WIN32 +#if HAVE_INET_PTON +#include <arpa/inet.h> +#endif #include <fcntl.h> #endif @@ -25,7 +36,7 @@ using namespace std; // Settings static proxyType proxyInfo[NET_MAX]; -static proxyType nameproxyInfo; +static CService nameProxy; static CCriticalSection cs_proxyInfos; int nConnectTimeout = 5000; bool fNameLookup = false; @@ -71,9 +82,30 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign } } +#ifdef HAVE_GETADDRINFO_A + struct in_addr ipv4_addr; +#ifdef HAVE_INET_PTON + if (inet_pton(AF_INET, pszName, &ipv4_addr) > 0) { + vIP.push_back(CNetAddr(ipv4_addr)); + return true; + } + + struct in6_addr ipv6_addr; + if (inet_pton(AF_INET6, pszName, &ipv6_addr) > 0) { + vIP.push_back(CNetAddr(ipv6_addr)); + return true; + } +#else + ipv4_addr.s_addr = inet_addr(pszName); + if (ipv4_addr.s_addr != INADDR_NONE) { + vIP.push_back(CNetAddr(ipv4_addr)); + return true; + } +#endif +#endif + struct addrinfo aiHint; memset(&aiHint, 0, sizeof(struct addrinfo)); - aiHint.ai_socktype = SOCK_STREAM; aiHint.ai_protocol = IPPROTO_TCP; aiHint.ai_family = AF_UNSPEC; @@ -82,8 +114,33 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign #else aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; #endif + struct addrinfo *aiRes = NULL; +#ifdef HAVE_GETADDRINFO_A + struct gaicb gcb, *query = &gcb; + memset(query, 0, sizeof(struct gaicb)); + gcb.ar_name = pszName; + gcb.ar_request = &aiHint; + int nErr = getaddrinfo_a(GAI_NOWAIT, &query, 1, NULL); + if (nErr) + return false; + + do { + // Should set the timeout limit to a resonable value to avoid + // generating unnecessary checking call during the polling loop, + // while it can still response to stop request quick enough. + // 2 seconds looks fine in our situation. + struct timespec ts = { 2, 0 }; + gai_suspend(&query, 1, &ts); + boost::this_thread::interruption_point(); + + nErr = gai_error(query); + if (0 == nErr) + aiRes = query->ar_result; + } while (nErr == EAI_INPROGRESS); +#else int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); +#endif if (nErr) return false; @@ -156,50 +213,6 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault) return Lookup(pszName, addr, portDefault, false); } -bool static Socks4(const CService &addrDest, SOCKET& hSocket) -{ - LogPrintf("SOCKS4 connecting %s\n", addrDest.ToString()); - if (!addrDest.IsIPv4()) - { - closesocket(hSocket); - return error("Proxy destination is not IPv4"); - } - char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET) - { - closesocket(hSocket); - return error("Cannot get proxy destination address"); - } - memcpy(pszSocks4IP + 2, &addr.sin_port, 2); - memcpy(pszSocks4IP + 4, &addr.sin_addr, 4); - char* pszSocks4 = pszSocks4IP; - int nSize = sizeof(pszSocks4IP); - - int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); - if (ret != nSize) - { - closesocket(hSocket); - return error("Error sending to proxy"); - } - char pchRet[8]; - if (recv(hSocket, pchRet, 8, 0) != 8) - { - closesocket(hSocket); - return error("Error reading proxy response"); - } - if (pchRet[1] != 0x5a) - { - closesocket(hSocket); - if (pchRet[1] != 0x5b) - LogPrintf("ERROR: Proxy returned error %d\n", pchRet[1]); - return false; - } - LogPrintf("SOCKS4 connected %s\n", addrDest.ToString()); - return true; -} - bool static Socks5(string strDest, int port, SOCKET& hSocket) { LogPrintf("SOCKS5 connecting %s\n", strDest); @@ -337,8 +350,9 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) { + int nErr = WSAGetLastError(); // WSAEINVAL is here because some legacy version of winsock uses it - if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) + if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { struct timeval timeout; timeout.tv_sec = nTimeout / 1000; @@ -409,53 +423,49 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe return true; } -bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { +bool SetProxy(enum Network net, CService addrProxy) { assert(net >= 0 && net < NET_MAX); - if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5) - return false; - if (nSocksVersion != 0 && !addrProxy.IsValid()) + if (!addrProxy.IsValid()) return false; LOCK(cs_proxyInfos); - proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); + proxyInfo[net] = addrProxy; return true; } bool GetProxy(enum Network net, proxyType &proxyInfoOut) { assert(net >= 0 && net < NET_MAX); LOCK(cs_proxyInfos); - if (!proxyInfo[net].second) + if (!proxyInfo[net].IsValid()) return false; proxyInfoOut = proxyInfo[net]; return true; } -bool SetNameProxy(CService addrProxy, int nSocksVersion) { - if (nSocksVersion != 0 && nSocksVersion != 5) - return false; - if (nSocksVersion != 0 && !addrProxy.IsValid()) +bool SetNameProxy(CService addrProxy) { + if (!addrProxy.IsValid()) return false; LOCK(cs_proxyInfos); - nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); + nameProxy = addrProxy; return true; } -bool GetNameProxy(proxyType &nameproxyInfoOut) { +bool GetNameProxy(CService &nameProxyOut) { LOCK(cs_proxyInfos); - if (!nameproxyInfo.second) + if(!nameProxy.IsValid()) return false; - nameproxyInfoOut = nameproxyInfo; + nameProxyOut = nameProxy; return true; } bool HaveNameProxy() { LOCK(cs_proxyInfos); - return nameproxyInfo.second != 0; + return nameProxy.IsValid(); } bool IsProxy(const CNetAddr &addr) { LOCK(cs_proxyInfos); for (int i = 0; i < NET_MAX; i++) { - if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) + if (addr == (CNetAddr)proxyInfo[i]) return true; } return false; @@ -464,31 +474,18 @@ bool IsProxy(const CNetAddr &addr) { bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout) { proxyType proxy; - - // no proxy needed + // no proxy needed (none set for target network) if (!GetProxy(addrDest.GetNetwork(), proxy)) return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); SOCKET hSocket = INVALID_SOCKET; // first connect to proxy server - if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout)) + if (!ConnectSocketDirectly(proxy, hSocket, nTimeout)) return false; - // do socks negotiation - switch (proxy.second) { - case 4: - if (!Socks4(addrDest, hSocket)) - return false; - break; - case 5: - if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) - return false; - break; - default: - closesocket(hSocket); + if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) return false; - } hSocketRet = hSocket; return true; @@ -502,30 +499,25 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest SOCKET hSocket = INVALID_SOCKET; - proxyType nameproxy; - GetNameProxy(nameproxy); + CService nameProxy; + GetNameProxy(nameProxy); - CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port); + CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port); if (addrResolved.IsValid()) { addr = addrResolved; return ConnectSocket(addr, hSocketRet, nTimeout); } + addr = CService("0.0.0.0:0"); - if (!nameproxy.second) + + if (!HaveNameProxy()) return false; - if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout)) + // first connect to name proxy server + if (!ConnectSocketDirectly(nameProxy, hSocket, nTimeout)) + return false; + // do socks negotiation + if (!Socks5(strDest, (unsigned short)port, hSocket)) return false; - - switch(nameproxy.second) { - default: - case 4: - closesocket(hSocket); - return false; - case 5: - if (!Socks5(strDest, port, hSocket)) - return false; - break; - } hSocketRet = hSocket; return true; @@ -1260,7 +1252,8 @@ std::string NetworkErrorString(int err) #ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */ s = strerror_r(err, buf, sizeof(buf)); #else /* POSIX variant always returns message in buffer */ - (void) strerror_r(err, buf, sizeof(buf)); + if (strerror_r(err, buf, sizeof(buf))) + buf[0] = 0; #endif return strprintf("%s (%d)", s, err); } diff --git a/src/netbase.h b/src/netbase.h index 23cfb1f158..ad1e230834 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -6,7 +6,7 @@ #define BITCOIN_NETBASE_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "compat.h" @@ -161,14 +161,14 @@ class CService : public CNetAddr ) }; -typedef std::pair<CService, int> proxyType; +typedef CService proxyType; enum Network ParseNetwork(std::string net); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); -bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); +bool SetProxy(enum Network net, CService addrProxy); bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); -bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); +bool SetNameProxy(CService addrProxy); bool HaveNameProxy(); bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); diff --git a/src/pow.cpp b/src/pow.cpp new file mode 100644 index 0000000000..c0d0a7ca20 --- /dev/null +++ b/src/pow.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "pow.h" + +#include "chainparams.h" +#include "core.h" +#include "main.h" +#include "uint256.h" + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) +{ + unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact(); + + // Genesis block + if (pindexLast == NULL) + return nProofOfWorkLimit; + + // Only change once per interval + if ((pindexLast->nHeight+1) % Params().Interval() != 0) + { + if (Params().AllowMinDifficultyBlocks()) + { + // Special difficulty rule for testnet: + // If the new block's timestamp is more than 2* 10 minutes + // then allow mining of a min-difficulty block. + if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + Params().TargetSpacing()*2) + return nProofOfWorkLimit; + else + { + // Return the last non-special-min-difficulty-rules-block + const CBlockIndex* pindex = pindexLast; + while (pindex->pprev && pindex->nHeight % Params().Interval() != 0 && pindex->nBits == nProofOfWorkLimit) + pindex = pindex->pprev; + return pindex->nBits; + } + } + return pindexLast->nBits; + } + + // Go back by what we want to be 14 days worth of blocks + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < Params().Interval()-1; i++) + pindexFirst = pindexFirst->pprev; + assert(pindexFirst); + + // Limit adjustment step + int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); + LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); + if (nActualTimespan < Params().TargetTimespan()/4) + nActualTimespan = Params().TargetTimespan()/4; + if (nActualTimespan > Params().TargetTimespan()*4) + nActualTimespan = Params().TargetTimespan()*4; + + // Retarget + uint256 bnNew; + uint256 bnOld; + bnNew.SetCompact(pindexLast->nBits); + bnOld = bnNew; + bnNew *= nActualTimespan; + bnNew /= Params().TargetTimespan(); + + if (bnNew > Params().ProofOfWorkLimit()) + bnNew = Params().ProofOfWorkLimit(); + + /// debug print + LogPrintf("GetNextWorkRequired RETARGET\n"); + LogPrintf("Params().TargetTimespan() = %d nActualTimespan = %d\n", Params().TargetTimespan(), nActualTimespan); + LogPrintf("Before: %08x %s\n", pindexLast->nBits, bnOld.ToString()); + LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString()); + + return bnNew.GetCompact(); +} + +bool CheckProofOfWork(uint256 hash, unsigned int nBits) +{ + bool fNegative; + bool fOverflow; + uint256 bnTarget; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); + + // Check range + if (fNegative || bnTarget == 0 || fOverflow || bnTarget > Params().ProofOfWorkLimit()) + return error("CheckProofOfWork() : nBits below minimum work"); + + // Check proof of work matches claimed amount + if (hash > bnTarget) + return error("CheckProofOfWork() : hash doesn't match nBits"); + + return true; +} + +// +// minimum amount of work that could possibly be required nTime after +// minimum work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime) +{ + const uint256 &bnLimit = Params().ProofOfWorkLimit(); + // Testnet has min-difficulty blocks + // after Params().TargetSpacing()*2 time between blocks: + if (Params().AllowMinDifficultyBlocks() && nTime > Params().TargetSpacing()*2) + return bnLimit.GetCompact(); + + uint256 bnResult; + bnResult.SetCompact(nBase); + while (nTime > 0 && bnResult < bnLimit) + { + // Maximum 400% adjustment... + bnResult *= 4; + // ... in best-case exactly 4-times-normal target time + nTime -= Params().TargetTimespan()*4; + } + if (bnResult > bnLimit) + bnResult = bnLimit; + return bnResult.GetCompact(); +} diff --git a/src/pow.h b/src/pow.h new file mode 100644 index 0000000000..0ce5b48766 --- /dev/null +++ b/src/pow.h @@ -0,0 +1,23 @@ + +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POW_H +#define BITCOIN_POW_H + +#include <stdint.h> + +class CBlockIndex; +class CBlockHeader; +class uint256; + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock); + +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime); + +#endif diff --git a/src/protocol.h b/src/protocol.h index 6de5d05a72..1f23274299 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -64,6 +64,14 @@ class CMessageHeader enum { NODE_NETWORK = (1 << 0), + + // Bits 24-31 are reserved for temporary experiments. Just pick a bit that + // isn't getting used, or one not being used much, and notify the + // bitcoin-development mailing list. Remember that service bits are just + // unauthenticated advertisements, so your code must be robust against + // collisions and other cases where nodes may be advertising a service they + // do not actually support. Other service bits should be allocated via the + // BIP process. }; /** A CService with information about it as peer */ diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 2dc56a5107..5df8f19729 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "addressbookpage.h" diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index dfbd445ce3..8d5284d5e9 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -114,7 +114,7 @@ public: case CT_NEW: if(inModel) { - qDebug() << "AddressTablePriv::updateEntry : Warning: Got CT_NEW, but entry is already in model"; + qWarning() << "AddressTablePriv::updateEntry : Warning: Got CT_NEW, but entry is already in model"; break; } parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); @@ -124,7 +124,7 @@ public: case CT_UPDATED: if(!inModel) { - qDebug() << "AddressTablePriv::updateEntry : Warning: Got CT_UPDATED, but entry is not in model"; + qWarning() << "AddressTablePriv::updateEntry : Warning: Got CT_UPDATED, but entry is not in model"; break; } lower->type = newEntryType; @@ -134,7 +134,7 @@ public: case CT_DELETED: if(!inModel) { - qDebug() << "AddressTablePriv::updateEntry : Warning: Got CT_DELETED, but entry is not in model"; + qWarning() << "AddressTablePriv::updateEntry : Warning: Got CT_DELETED, but entry is not in model"; break; } parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 2a6d6abc35..a448d5a9a0 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -37,7 +37,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : case Encrypt: // Ask passphrase x2 ui->passLabel1->hide(); ui->passEdit1->hide(); - ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.")); + ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.")); setWindowTitle(tr("Encrypt wallet")); break; case Unlock: // Ask passphrase diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 387f6ede4b..7c4af25edf 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "bitcoingui.h" @@ -126,15 +126,15 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans #if QT_VERSION < 0x050000 void DebugMessageHandler(QtMsgType type, const char *msg) { - Q_UNUSED(type); - LogPrint("qt", "GUI: %s\n", msg); + const char *category = (type == QtDebugMsg) ? "qt" : NULL; + LogPrint(category, "GUI: %s\n", msg); } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { - Q_UNUSED(type); Q_UNUSED(context); - LogPrint("qt", "GUI: %s\n", qPrintable(msg)); + const char *category = (type == QtDebugMsg) ? "qt" : NULL; + LogPrint(category, "GUI: %s\n", msg.toStdString()); } #endif @@ -546,7 +546,7 @@ int main(int argc, char *argv[]) if (!PaymentServer::ipcParseCommandLine(argc, argv)) exit(0); #endif - bool isaTestNet = Params().NetworkID() != CChainParams::MAIN; + bool isaTestNet = Params().NetworkID() != CBaseChainParams::MAIN; // Allow for separate UI settings for testnets if (isaTestNet) QApplication::setApplicationName(QAPP_APP_NAME_TESTNET); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index f38200c7f7..357c6470d3 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -35,6 +35,9 @@ <file alias="tx_input">res/icons/tx_input.png</file> <file alias="tx_output">res/icons/tx_output.png</file> <file alias="tx_inout">res/icons/tx_inout.png</file> + <file alias="unit_btc">res/icons/unit_btc.png</file> + <file alias="unit_mbtc">res/icons/unit_mbtc.png</file> + <file alias="unit_ubtc">res/icons/unit_ubtc.png</file> <file alias="lock_closed">res/icons/lock_closed.png</file> <file alias="lock_open">res/icons/lock_open.png</file> <file alias="key">res/icons/key.png</file> diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 30f5ec8939..6b3aa2a2df 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -28,14 +28,13 @@ #include <iostream> +#include <QAction> #include <QApplication> #include <QDateTime> #include <QDesktopWidget> #include <QDragEnterEvent> #include <QIcon> -#include <QLabel> #include <QListWidget> -#include <QMenu> #include <QMenuBar> #include <QMessageBox> #include <QMimeData> @@ -156,10 +155,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); + unitDisplayControl = new UnitDisplayStatusBarControl(); labelEncryptionIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(unitDisplayControl); + frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelEncryptionIcon); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelConnectionsIcon); @@ -420,6 +422,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) walletFrame->setClientModel(clientModel); } #endif + + this->unitDisplayControl->setOptionsModel(clientModel->getOptionsModel()); } } @@ -1000,3 +1004,71 @@ void BitcoinGUI::unsubscribeFromCoreSignals() // Disconnect signals from client uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); } + +UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel() +{ + optionsModel = 0; + createContextMenu(); + setToolTip(tr("Unit to show amounts in. Click to select another unit.")); +} + +/** So that it responds to left-button clicks */ +void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) +{ + onDisplayUnitsClicked(event->pos()); +} + +/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */ +void UnitDisplayStatusBarControl::createContextMenu() +{ + menu = new QMenu(); + foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) + { + QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this); + menuAction->setData(QVariant(u)); + menu->addAction(menuAction); + } + connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*))); + + // what happens on right click. + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&))); +} + +/** Lets the control know about the Options Model (and its signals) */ +void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel) +{ + if (optionsModel) + { + this->optionsModel = optionsModel; + + // be aware of a display unit change reported by the OptionsModel object. + connect(optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int))); + + // initialize the display units label with the current value in the model. + updateDisplayUnit(optionsModel->getDisplayUnit()); + } +} + +/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ +void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) +{ + setPixmap(QIcon(":/icons/unit_" + BitcoinUnits::id(newUnits)).pixmap(31,STATUSBAR_ICONSIZE)); +} + +/** Shows context menu with Display Unit options by the mouse coordinates */ +void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point) +{ + QPoint globalPos = mapToGlobal(point); + menu->exec(globalPos); +} + +/** Tells underlying optionsModel to update its current display unit. */ +void UnitDisplayStatusBarControl::onMenuSelection(QAction* action) +{ + if (action) + { + optionsModel->setDisplayUnit(action->data()); + } +} + diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 275fa35f39..30dd7ae317 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,17 +6,22 @@ #define BITCOINGUI_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif +#include <QLabel> #include <QMainWindow> #include <QMap> +#include <QMenu> +#include <QPoint> #include <QSystemTrayIcon> class ClientModel; class Notificator; +class OptionsModel; class RPCConsole; class SendCoinsRecipient; +class UnitDisplayStatusBarControl; class WalletFrame; class WalletModel; @@ -24,7 +29,6 @@ class CWallet; QT_BEGIN_NAMESPACE class QAction; -class QLabel; class QProgressBar; class QProgressDialog; QT_END_NAMESPACE @@ -69,6 +73,7 @@ private: ClientModel *clientModel; WalletFrame *walletFrame; + UnitDisplayStatusBarControl *unitDisplayControl; QLabel *labelEncryptionIcon; QLabel *labelConnectionsIcon; QLabel *labelBlocksIcon; @@ -198,4 +203,32 @@ private slots: void showProgress(const QString &title, int nProgress); }; +class UnitDisplayStatusBarControl : public QLabel +{ + Q_OBJECT + +public: + explicit UnitDisplayStatusBarControl(); + /** Lets the control know about the Options Model (and its signals) */ + void setOptionsModel(OptionsModel *optionsModel); + +protected: + /** So that it responds to left-button clicks */ + void mousePressEvent(QMouseEvent *event); + +private: + OptionsModel *optionsModel; + QMenu* menu; + /** Shows context menu with Display Unit options by the mouse coordinates */ + void onDisplayUnitsClicked(const QPoint& point); + /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */ + void createContextMenu(); + +private slots: + /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ + void updateDisplayUnit(int newUnits); + /** Tells underlying optionsModel to update its current display unit. */ + void onMenuSelection(QAction* action); +}; + #endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 10b44bbc3f..e852c468a8 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -22,31 +22,42 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "It is also recommended to set alertnotify so you are notified of problems;\n" "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"(default: 1, 1 = keep tx meta data e.g. account owner and payment request " +"information, 2 = drop tx meta data)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!" "3DES:@STRENGTH)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +"Allow JSON-RPC connections from specified source. Valid for <ip> are a " +"single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or " +"a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"An error occurred while setting up the RPC port %u for listening on IPv6, " -"falling back to IPv4: %s"), +"An error occurred while setting up the RPC address %s port %u for listening: " +"%s"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Bind to given address and always listen on it. Use [host]:port notation for " "IPv6"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Bind to given address to listen for JSON-RPC connections. Use [host]:port " +"notation for IPv6. This option can be specified multiple times (default: " +"bind to all interfaces)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Cannot obtain a lock on data directory %s. Bitcoin Core is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Continuously rate-limit free transactions to <n>*1000 bytes per minute " "(default:15)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Enter regression test mode, which uses a special chain in which blocks can " -"be solved instantly. This is intended for regression testing tools and app " -"development."), +"Delete all wallet transactions and only recover those part of the blockchain " +"through -rescan on startup"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Distributed under the MIT/X11 software license, see the accompanying file " +"COPYING or <http://www.opensource.org/licenses/mit-license.php>."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Enter regression test mode, which uses a special chain in which blocks can " "be solved instantly."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: Listening for incoming connections failed (listen returned error %d)"), +"Error: Listening for incoming connections failed (listen returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: The transaction was rejected! This might happen if some of the coins " "in your wallet were already spent, such as if you used a copy of wallet.dat " @@ -55,6 +66,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: This transaction requires a transaction fee of at least %s because of " "its amount, complexity, or use of recently received funds!"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Execute command when a network tx respends wallet tx input (%s=respend TxID, " +"%t=wallet TxID)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when a relevant alert is received or we see a really long " "fork (%s in cmd is replaced by message)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -64,14 +78,20 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when the best block changes (%s in cmd is replaced by block " "hash)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Fees smaller than this are considered zero fee (for transaction creation) " -"(default:"), +"Fees (in BTC/Kb) smaller than this are considered zero fee for relaying " +"(default: %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Fees (in BTC/Kb) smaller than this are considered zero fee for transaction " +"creation (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Flush database activity from memory pool to disk log every <n> megabytes " "(default: 100)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "How thorough the block verification of -checkblocks is (0-4, default: 3)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"If paytxfee is not set, include enough fee so transactions are confirmed on " +"average within n blocks (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "In this mode -genproclimit controls how many blocks are generated " "immediately."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -93,6 +113,10 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "This is a pre-release test build - use at your own risk - do not use for " "mining or merchant applications"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"This product includes software developed by the OpenSSL Project for use in " +"the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software " +"written by Eric Young and UPnP software written by Thomas Bernard."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Unable to bind to %s on this computer. Bitcoin Core is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -117,11 +141,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as " "wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect " "you should restore from a backup."), -QT_TRANSLATE_NOOP("bitcoin-core", "" -"You must set rpcpassword=<password> in the configuration file:\n" -"%s\n" -"If the file does not exist, create it with owner-readable-only file " -"permissions."), QT_TRANSLATE_NOOP("bitcoin-core", "(default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: wallet.dat)"), QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), @@ -129,22 +148,19 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), -QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"), QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), -QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core Daemon"), -QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core RPC client version"), QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), -QT_TRANSLATE_NOOP("bitcoin-core", "Clear list of wallet transactions (diagnostic tool; implies -rescan)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect through SOCKS proxy"), -QT_TRANSLATE_NOOP("bitcoin-core", "Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), QT_TRANSLATE_NOOP("bitcoin-core", "Connection options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) 2009-%i The Bitcoin Core Developers"), QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"), +QT_TRANSLATE_NOOP("bitcoin-core", "Could not parse -rpcbind value %s as network address"), QT_TRANSLATE_NOOP("bitcoin-core", "Debugging/Testing options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Disable safemode, override a real safe mode event (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), @@ -160,6 +176,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires new QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unsupported argument -tor found, use -onion."), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), @@ -173,27 +190,28 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), -QT_TRANSLATE_NOOP("bitcoin-core", "Fee per kB to add to transactions you send"), -QT_TRANSLATE_NOOP("bitcoin-core", "Fees smaller than this are considered zero fee (for relaying) (default:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in BTC/kB) to add to transactions you send (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Force safe mode (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: 0)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "If <category> is not supplied, output all debugging information."), QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Include IP addresses in debug output (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), QT_TRANSLATE_NOOP("bitcoin-core", "Information"), +QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. Bitcoin Core is shutting down."), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=<amount>: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), +QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable blocks in memory (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Limit size of signature cache to <n> entries (default: 50000)"), -QT_TRANSLATE_NOOP("bitcoin-core", "List commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: 8333 or testnet: 18333)"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), @@ -203,6 +221,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Maintain a full transaction index (default: 0 QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most <n> connections to peers (default: 125)"), QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)"), QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Node relay options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."), QT_TRANSLATE_NOOP("bitcoin-core", "Only accept block chain matching built-in checkpoints (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (IPv4, IPv6 or Tor)"), @@ -212,19 +231,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: QT_TRANSLATE_NOOP("bitcoin-core", "Print block on startup, if found in block index"), QT_TRANSLATE_NOOP("bitcoin-core", "Print block tree on startup (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)"), -QT_TRANSLATE_NOOP("bitcoin-core", "RPC client options:"), QT_TRANSLATE_NOOP("bitcoin-core", "RPC server options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Randomly drop 1 of every <n> network messages"), QT_TRANSLATE_NOOP("bitcoin-core", "Randomly fuzz 1 of every <n> network messages"), QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files"), +QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), QT_TRANSLATE_NOOP("bitcoin-core", "Run a thread to flush wallet periodically (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), -QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the Bitcoin Wiki for SSL setup instructions)"), QT_TRANSLATE_NOOP("bitcoin-core", "Select SOCKS version for -proxy (4 or 5, default: 5)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Send command to Bitcoin Core"), -QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on <ip> (default: 127.0.0.1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)"), QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)"), @@ -245,21 +261,20 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: 1)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Start Bitcoin Core Daemon"), +QT_TRANSLATE_NOOP("bitcoin-core", "Stop running after importing blocks from disk (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), +QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."), QT_TRANSLATE_NOOP("bitcoin-core", "This is intended for regression testing tools and app development."), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must be positive"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), -QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %d, %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), -QT_TRANSLATE_NOOP("bitcoin-core", "Usage (deprecated, use bitcoin-cli):"), -QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 1 when listening)"), @@ -267,7 +282,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"), QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), -QT_TRANSLATE_NOOP("bitcoin-core", "Wait for RPC server to start"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet options:"), @@ -277,6 +291,5 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade re QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."), QT_TRANSLATE_NOOP("bitcoin-core", "on startup"), -QT_TRANSLATE_NOOP("bitcoin-core", "version"), QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), }; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 2fed443cf2..bbc9b2e5af 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -34,6 +34,17 @@ bool BitcoinUnits::valid(int unit) } } +QString BitcoinUnits::id(int unit) +{ + switch(unit) + { + case BTC: return QString("btc"); + case mBTC: return QString("mbtc"); + case uBTC: return QString("ubtc"); + default: return QString("???"); + } +} + QString BitcoinUnits::name(int unit) { switch(unit) @@ -169,6 +180,16 @@ bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) return ok; } +QString BitcoinUnits::getAmountColumnTitle(int unit) +{ + QString amountTitle = QObject::tr("Amount"); + if (BitcoinUnits::valid(unit)) + { + amountTitle += " ("+BitcoinUnits::name(unit) + ")"; + } + return amountTitle; +} + int BitcoinUnits::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 46517fc07b..da34ed8976 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -36,6 +36,8 @@ public: static QList<Unit> availableUnits(); //! Is unit ID valid? static bool valid(int unit); + //! Identifier, e.g. for image names + static QString id(int unit); //! Short name static QString name(int unit); //! Longer description @@ -54,6 +56,8 @@ public: static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); //! Parse string to coin amount static bool parse(int unit, const QString &value, qint64 *val_out); + //! Gets title for amount column including current display unit if optionsModel reference available */ + static QString getAmountColumnTitle(int unit); ///@} //! @name AbstractListModel implementation diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 403b03378f..4c21eb5594 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -85,7 +85,7 @@ QDateTime ClientModel::getLastBlockDate() const if (chainActive.Tip()) return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else - return QDateTime::fromTime_t(Params().GenesisBlock().nTime); // Genesis block's time of current network + return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network } double ClientModel::getVerificationProgress() const @@ -142,14 +142,6 @@ void ClientModel::updateAlert(const QString &hash, int status) emit alertsChanged(getStatusBarWarnings()); } -QString ClientModel::getNetworkName() const -{ - QString netname(QString::fromStdString(Params().DataDir())); - if(netname.isEmpty()) - netname = "main"; - return netname; -} - bool ClientModel::inInitialBlockDownload() const { return IsInitialBlockDownload(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 9c9a35b654..c7bd60bd41 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -56,8 +56,6 @@ public: double getVerificationProgress() const; QDateTime getLastBlockDate() const; - //! Return network (main, testnet3, regtest) - QString getNetworkName() const; //! Return true if core is doing initial block download bool inInitialBlockDownload() const; //! Return true if core is importing blocks diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 42d6da7d37..b4ddda3eaa 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -16,6 +16,8 @@ #include "main.h" #include "wallet.h" +#include <boost/assign/list_of.hpp> // for 'map_list_of()' + #include <QApplication> #include <QCheckBox> #include <QCursor> @@ -400,23 +402,26 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) } // return human readable label for priority number -QString CoinControlDialog::getPriorityLabel(double dPriority) +QString CoinControlDialog::getPriorityLabel(const CTxMemPool& pool, double dPriority) { - if (AllowFree(dPriority)) // at least medium + // confirmations -> textual description + typedef std::map<unsigned int, QString> PriorityDescription; + const static PriorityDescription priorityDescriptions = boost::assign::map_list_of + (1, tr("highest"))(2, tr("higher"))(3, tr("high")) + (5, tr("medium-high"))(6, tr("medium")) + (10, tr("low-medium"))(15, tr("low")) + (20, tr("lower")); + + BOOST_FOREACH(const PriorityDescription::value_type& i, priorityDescriptions) { - if (AllowFree(dPriority / 1000000)) return tr("highest"); - else if (AllowFree(dPriority / 100000)) return tr("higher"); - else if (AllowFree(dPriority / 10000)) return tr("high"); - else if (AllowFree(dPriority / 1000)) return tr("medium-high"); - else return tr("medium"); - } - else - { - if (AllowFree(dPriority * 10)) return tr("low-medium"); - else if (AllowFree(dPriority * 100)) return tr("low"); - else if (AllowFree(dPriority * 1000)) return tr("lower"); - else return tr("lowest"); + double p = mempool.estimatePriority(i.first); + if (p > 0 && dPriority >= p) return i.second; } + // Note: if mempool hasn't accumulated enough history (estimatePriority + // returns -1) we're conservative and classify as "lowest" + if (mempool.estimatePriority(nTxConfirmTarget) <= 0 && AllowFree(dPriority)) + return ">=" + tr("medium"); + return tr("lowest"); } // shows count of locked unspent outputs @@ -440,7 +445,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // nPayAmount qint64 nPayAmount = 0; bool fDust = false; - CTransaction txDummy; + CMutableTransaction txDummy; foreach(const qint64 &amount, CoinControlDialog::payAmounts) { nPayAmount += amount; @@ -449,7 +454,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0)); txDummy.vout.push_back(txout); - if (txout.IsDust(CTransaction::minRelayTxFee)) + if (txout.IsDust(::minRelayTxFee)) fDust = true; } } @@ -518,15 +523,23 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Priority dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) - sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); + sPriorityLabel = CoinControlDialog::getPriorityLabel(mempool, dPriority); - // Fee - int64_t nFee = payTxFee.GetFee(nBytes); + // Voluntary Fee + nPayFee = payTxFee.GetFee(max((unsigned int)1000, nBytes)); // Min Fee - int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND); + if (nPayFee == 0) + { + nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); - nPayFee = max(nFee, nMinFee); + double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); + if (dPriorityNeeded <= 0 && !AllowFree(dPriority)) // not enough mempool history: never send free + dPriorityNeeded = std::numeric_limits<double>::max(); + + if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded) + nPayFee = 0; + } if (nPayAmount > 0) { @@ -536,7 +549,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < CENT) { CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0)); - if (txout.IsDust(CTransaction::minRelayTxFee)) + if (txout.IsDust(::minRelayTxFee)) { nPayFee += nChange; nChange = 0; @@ -582,29 +595,46 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l6->setText(sPriorityLabel); // Priority l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change + if (nPayFee > 0) + { + l3->setText("~" + l3->text()); + l4->setText("~" + l4->text()); + if (nChange > 0) + l8->setText("~" + l8->text()); + } // turn labels "red" - l5->setStyleSheet((nBytes >= 1000) ? "color:red;" : ""); // Bytes >= 1000 + l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000 l6->setStyleSheet((dPriority > 0 && !AllowFree(dPriority)) ? "color:red;" : ""); // Priority < "medium" l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes" // tool tips QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />"; - toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK())) + "<br /><br />"; + toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK())) + "<br /><br />"; toolTip1 += tr("Can vary +/- 1 byte per input."); QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />"; toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\".") + "<br /><br />"; - toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK())); + toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK())); + + QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546))); - QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minRelayTxFee.GetFee(546))); + // how many satoshis the estimated fee can vary per byte we guess wrong + double dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), std::max(payTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK())) / 1000; + QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); + l3->setToolTip(toolTip4); + l4->setToolTip(toolTip4); l5->setToolTip(toolTip1); l6->setToolTip(toolTip2); l7->setToolTip(toolTip3); + l8->setToolTip(toolTip4); + dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip()); + dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip()); dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); + dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); // Insufficient funds QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds"); @@ -715,7 +745,7 @@ void CoinControlDialog::updateView() // priority double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 - itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority)); + itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPriority)); itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " ")); dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); nInputSum += nInputSize; @@ -748,7 +778,7 @@ void CoinControlDialog::updateView() itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); - itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum)); + itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPrioritySum)); itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " ")); } } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 465e2a009d..4f7422642f 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -19,6 +19,7 @@ namespace Ui { } class WalletModel; class CCoinControl; +class CTxMemPool; class CoinControlDialog : public QDialog { @@ -32,7 +33,7 @@ public: // static because also called from sendcoinsdialog static void updateLabels(WalletModel*, QDialog*); - static QString getPriorityLabel(double); + static QString getPriorityLabel(const CTxMemPool& pool, double); static QList<qint64> payAmounts; static CCoinControl *coinControl; diff --git a/src/qt/forms/helpmessagedialog.ui b/src/qt/forms/helpmessagedialog.ui index d8ab27c238..81dbd90b12 100644 --- a/src/qt/forms/helpmessagedialog.ui +++ b/src/qt/forms/helpmessagedialog.ui @@ -60,6 +60,9 @@ <property name="textFormat"> <enum>Qt::PlainText</enum> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 0c5b8895aa..1f535a4a62 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -329,26 +329,6 @@ </widget> </item> <item> - <widget class="QLabel" name="socksVersionLabel"> - <property name="text"> - <string>SOCKS &Version:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="buddy"> - <cstring>socksVersion</cstring> - </property> - </widget> - </item> - <item> - <widget class="QValueComboBox" name="socksVersion"> - <property name="toolTip"> - <string>SOCKS version of the proxy (e.g. 5)</string> - </property> - </widget> - </item> - <item> <spacer name="horizontalSpacer_1_Network"> <property name="orientation"> <enum>Qt::Horizontal</enum> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index e662912781..8784da5f3e 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>573</width> + <width>596</width> <height>342</height> </rect> </property> @@ -46,204 +46,369 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Wallet</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="labelWalletStatus"> - <property name="toolTip"> - <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> - </property> - <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> - </property> - <property name="text"> - <string notr="true">(out of sync)</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QLabel" name="label_5"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Wallet</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelWalletStatus"> + <property name="toolTip"> + <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color: red; }</string> + </property> + <property name="text"> + <string notr="true">(out of sync)</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> </item> <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QLabel" name="labelWatchonly"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Watchonly:</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> </item> </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <layout class="QFormLayout" name="formLayout_2"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <property name="horizontalSpacing"> - <number>12</number> - </property> - <property name="verticalSpacing"> - <number>12</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Available:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="labelBalance"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Your current spendable balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Pending:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="labelUnconfirmed"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="labelImmatureText"> - <property name="text"> - <string>Immature:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="labelImmature"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="toolTip"> - <string>Mined balance that has not yet matured</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="labelTotalText"> - <property name="text"> - <string>Total:</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QLabel" name="labelTotal"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="toolTip"> - <string>Your current total balance</string> - </property> - <property name="text"> - <string notr="true">0 BTC</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QFormLayout" name="formLayout_2"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <property name="horizontalSpacing"> + <number>12</number> + </property> + <property name="verticalSpacing"> + <number>12</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Available:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="labelBalance"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Your current spendable balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Pending:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="labelUnconfirmed"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="labelImmatureText"> + <property name="text"> + <string>Immature:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="labelImmature"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Mined balance that has not yet matured</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="labelTotalText"> + <property name="text"> + <string>Total:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="labelTotal"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Your current total balance</string> + </property> + <property name="text"> + <string notr="true">0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> </item> - <item row="3" column="0" colspan="2"> - <widget class="Line" name="line"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <property name="horizontalSpacing"> + <number>12</number> + </property> + <property name="verticalSpacing"> + <number>12</number> + </property> + <item row="0" column="1"> + <widget class="QLabel" name="labelWatchAvailable"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Your current balance in watchonly addresses</string> + </property> + <property name="text"> + <string>0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="labelWatchPending"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Unconfirmed transactions to watchonly addresses</string> + </property> + <property name="text"> + <string>0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="labelWatchImmature"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Mined balance in watchonly addresses that has not yet matured</string> + </property> + <property name="text"> + <string>0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="Line" name="lineWatchBalance"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>140</width> + <height>0</height> + </size> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="labelWatchTotal"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="toolTip"> + <string>Current total balance in watchonly addresses</string> + </property> + <property name="text"> + <string>0 BTC</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> </item> - </layout> - </item> <item> <spacer name="horizontalSpacer_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> <property name="sizeHint" stdset="0"> <size> - <width>40</width> + <width>20</width> <height>20</height> </size> </property> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 1e574e8527..7158b65c2d 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -559,7 +559,7 @@ <item> <widget class="QLabel" name="label_16"> <property name="text"> - <string>In:</string> + <string>Received</string> </property> </widget> </item> @@ -639,7 +639,7 @@ <item> <widget class="QLabel" name="label_17"> <property name="text"> - <string>Out:</string> + <string>Sent</string> </property> </widget> </item> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index e77de0d9b8..9d829970f0 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -51,7 +51,7 @@ <item> <widget class="QValidatedLineEdit" name="payTo"> <property name="toolTip"> - <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The Bitcoin address to send the payment to</string> </property> </widget> </item> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index aa271b4f2a..53573ec821 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -45,7 +45,7 @@ <item> <widget class="QValidatedLineEdit" name="addressIn_SM"> <property name="toolTip"> - <string>The address to sign the message with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The Bitcoin address to sign the message with</string> </property> </widget> </item> @@ -255,7 +255,7 @@ <item> <widget class="QValidatedLineEdit" name="addressIn_VM"> <property name="toolTip"> - <string>The address the message was signed with (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The Bitcoin address the message was signed with</string> </property> </widget> </item> diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 5ae4bc833d..696761e234 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -23,6 +23,10 @@ static const int STATUSBAR_ICONSIZE = 16; #define COLOR_NEGATIVE QColor(255, 0, 0) /* Transaction list -- bare address (without label) */ #define COLOR_BAREADDRESS QColor(140, 140, 140) +/* Transaction list -- has conflicting transactions */ +#define COLOR_HASCONFLICTING QColor(255, 255, 255) +/* Transaction list -- has conflicting transactions - background */ +#define COLOR_HASCONFLICTING_BG QColor(192, 0, 0) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 4fe98251d9..60a131df7e 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,6 +11,7 @@ #include "core.h" #include "init.h" +#include "main.h" #include "protocol.h" #include "util.h" @@ -91,7 +92,9 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) widget->setFont(bitcoinAddressFont()); #if QT_VERSION >= 0x040700 - widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); + // We don't want translators to use own addresses in translations + // and this is the only place, where this address is supplied. + widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg("1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L")); #endif widget->setValidator(new BitcoinAddressEntryValidator(parent)); widget->setCheckValidator(new BitcoinAddressCheckValidator(parent)); @@ -210,7 +213,7 @@ bool isDust(const QString& address, qint64 amount) CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CScript script; script.SetDestination(dest); CTxOut txOut(amount, script); - return txOut.IsDust(CTransaction::minRelayTxFee); + return txOut.IsDust(::minRelayTxFee); } QString HtmlEscape(const QString& str, bool fMultiLine) diff --git a/src/qt/notificator.h b/src/qt/notificator.h index abab986992..3395e64350 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -6,7 +6,7 @@ #define NOTIFICATOR_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <QIcon> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index abfd4123e0..597be40abd 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "optionsdialog.h" @@ -14,7 +14,10 @@ #include "monitoreddatamapper.h" #include "optionsmodel.h" -#include "main.h" // for CTransaction::minTxFee and MAX_SCRIPTCHECK_THREADS +#include "main.h" // for MAX_SCRIPTCHECK_THREADS +#ifdef ENABLE_WALLET +#include "wallet.h" // for CWallet::minTxFee +#endif #include "netbase.h" #include "txdb.h" // for -dbcache defaults @@ -49,15 +52,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) : ui->proxyPort->setEnabled(false); ui->proxyPort->setValidator(new QIntValidator(1, 65535, this)); - /** SOCKS version is only selectable for default proxy and is always 5 for IPv6 and Tor */ - ui->socksVersion->setEnabled(false); - ui->socksVersion->addItem("5", 5); - ui->socksVersion->addItem("4", 4); - ui->socksVersion->setCurrentIndex(0); - connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool))); connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool))); - connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool))); ui->proxyIp->installEventFilter(this); @@ -101,7 +97,9 @@ OptionsDialog::OptionsDialog(QWidget *parent) : #endif ui->unit->setModel(new BitcoinUnits(this)); - ui->transactionFee->setSingleStep(CTransaction::minTxFee.GetFeePerK()); +#ifdef ENABLE_WALLET + ui->transactionFee->setSingleStep(CWallet::minTxFee.GetFeePerK()); +#endif /* Widget-to-option mapper */ mapper = new MonitoredDataMapper(this); @@ -177,7 +175,6 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP); mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); - mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion); /* Window */ #ifndef Q_OS_MAC diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4dafd9d2af..f07e66bf04 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "optionsmodel.h" @@ -122,11 +122,6 @@ void OptionsModel::Init() // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) addOverriddenOption("-proxy"); - if (!settings.contains("nSocksVersion")) - settings.setValue("nSocksVersion", 5); - // Only try to set -socks, if user has enabled fUseProxy - if (settings.value("fUseProxy").toBool() && !SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString())) - addOverriddenOption("-socks"); // Display if (!settings.contains("language")) @@ -188,8 +183,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts); return strlIpPort.at(1); } - case ProxySocksVersion: - return settings.value("nSocksVersion", 5); #ifdef ENABLE_WALLET case Fee: { @@ -284,13 +277,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } } break; - case ProxySocksVersion: { - if (settings.value("nSocksVersion") != value) { - settings.setValue("nSocksVersion", value.toInt()); - setRestartRequired(true); - } - } - break; #ifdef ENABLE_WALLET case Fee: { // core option - can be changed on-the-fly // Todo: Add is valid check and warn via message, if not @@ -308,9 +294,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; #endif case DisplayUnit: - nDisplayUnit = value.toInt(); - settings.setValue("nDisplayUnit", nDisplayUnit); - emit displayUnitChanged(nDisplayUnit); + setDisplayUnit(value); break; case DisplayAddresses: bDisplayAddresses = value.toBool(); @@ -356,31 +340,40 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; } } + emit dataChanged(index, index); return successful; } +/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ +void OptionsModel::setDisplayUnit(const QVariant &value) +{ + if (!value.isNull()) + { + QSettings settings; + nDisplayUnit = value.toInt(); + settings.setValue("nDisplayUnit", nDisplayUnit); + emit displayUnitChanged(nDisplayUnit); + } +} + bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const { // Directly query current base proxy, because // GUI settings can be overridden with -proxy. proxyType curProxy; if (GetProxy(NET_IPV4, curProxy)) { - if (curProxy.second == 5) { - proxy.setType(QNetworkProxy::Socks5Proxy); - proxy.setHostName(QString::fromStdString(curProxy.first.ToStringIP())); - proxy.setPort(curProxy.first.GetPort()); + proxy.setType(QNetworkProxy::Socks5Proxy); + proxy.setHostName(QString::fromStdString(curProxy.ToStringIP())); + proxy.setPort(curProxy.GetPort()); - return true; - } - else - return false; + return true; } else proxy.setType(QNetworkProxy::NoProxy); - return true; + return false; } void OptionsModel::setRestartRequired(bool fRequired) diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 2596682d07..89c2ec7453 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -52,6 +52,8 @@ public: int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ + void setDisplayUnit(const QVariant &value); /* Explicit getters */ bool getMinimizeToTray() { return fMinimizeToTray; } diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1a9d1de571..1278f368cf 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -103,6 +103,9 @@ OverviewPage::OverviewPage(QWidget *parent) : currentBalance(-1), currentUnconfirmedBalance(-1), currentImmatureBalance(-1), + currentWatchOnlyBalance(-1), + currentWatchUnconfBalance(-1), + currentWatchImmatureBalance(-1), txdelegate(new TxViewDelegate()), filter(0) { @@ -135,22 +138,39 @@ OverviewPage::~OverviewPage() delete ui; } -void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); currentBalance = balance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; + currentWatchOnlyBalance = watchOnlyBalance; + currentWatchUnconfBalance = watchUnconfBalance; + currentWatchImmatureBalance = watchImmatureBalance; ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance)); + ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance)); + ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance)); + ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance)); + ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users bool showImmature = immatureBalance != 0; - ui->labelImmature->setVisible(showImmature); - ui->labelImmatureText->setVisible(showImmature); + bool showWatchOnlyImmature = watchImmatureBalance != 0; + bool showWatchOnly = (watchOnlyBalance != 0 || watchUnconfBalance != 0 || showWatchOnlyImmature); + + // for symmetry reasons also show immature label when the watchonly one is shown + ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); + ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); + ui->labelWatchonly->setVisible(showWatchOnly); // show Watchonly label + ui->lineWatchBalance->setVisible(showWatchOnly); // show watchonly balance separator line + ui->labelWatchAvailable->setVisible(showWatchOnly); // show watchonly available balance + ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watchonly immature balance + ui->labelWatchPending->setVisible(showWatchOnly); // show watchonly pending balance + ui->labelWatchTotal->setVisible(showWatchOnly); // show watchonly total balance } void OverviewPage::setClientModel(ClientModel *model) @@ -182,8 +202,9 @@ void OverviewPage::setWalletModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); - connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), + model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); } @@ -197,7 +218,8 @@ void OverviewPage::updateDisplayUnit() if(walletModel && walletModel->getOptionsModel()) { if(currentBalance != -1) - setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance); + setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance, + currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance); // Update txdelegate->unit with the current unit txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 2507a3fb31..fe00106770 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -34,7 +34,8 @@ public: void showOutOfSyncWarning(bool fShow); public slots: - void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); signals: void transactionClicked(const QModelIndex &index); @@ -46,6 +47,9 @@ private: qint64 currentBalance; qint64 currentUnconfirmedBalance; qint64 currentImmatureBalance; + qint64 currentWatchOnlyBalance; + qint64 currentWatchUnconfBalance; + qint64 currentWatchImmatureBalance; TxViewDelegate *txdelegate; TransactionFilterProxy *filter; diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index e369734a98..acce42e203 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -17,6 +17,7 @@ #include <QDebug> #include <QSslCertificate> +using namespace std; class SSLVerifyError : public std::runtime_error { @@ -28,18 +29,18 @@ bool PaymentRequestPlus::parse(const QByteArray& data) { bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size()); if (!parseOK) { - qDebug() << "PaymentRequestPlus::parse : Error parsing payment request"; + qWarning() << "PaymentRequestPlus::parse : Error parsing payment request"; return false; } if (paymentRequest.payment_details_version() > 1) { - qDebug() << "PaymentRequestPlus::parse : Received up-version payment details, version=" << paymentRequest.payment_details_version(); + qWarning() << "PaymentRequestPlus::parse : Received up-version payment details, version=" << paymentRequest.payment_details_version(); return false; } parseOK = details.ParseFromString(paymentRequest.serialized_payment_details()); if (!parseOK) { - qDebug() << "PaymentRequestPlus::parse : Error parsing payment details"; + qWarning() << "PaymentRequestPlus::parse : Error parsing payment details"; paymentRequest.Clear(); return false; } @@ -79,17 +80,17 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c digestAlgorithm = EVP_sha1(); } else if (paymentRequest.pki_type() == "none") { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; return false; } else { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type()); + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type()); return false; } payments::X509Certificates certChain; if (!certChain.ParseFromString(paymentRequest.pki_data())) { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error parsing pki_data"; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: error parsing pki_data"; return false; } @@ -99,12 +100,12 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size()); QSslCertificate qCert(certData, QSsl::Der); if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate expired or not yet active: " << qCert; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: certificate expired or not yet active: " << qCert; return false; } #if QT_VERSION >= 0x050000 if (qCert.isBlacklisted()) { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate blacklisted: " << qCert; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: certificate blacklisted: " << qCert; return false; } #endif @@ -114,7 +115,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c certs.push_back(cert); } if (certs.empty()) { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: empty certificate chain"; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: empty certificate chain"; return false; } @@ -130,7 +131,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c // load the signing cert into it and verify. X509_STORE_CTX *store_ctx = X509_STORE_CTX_new(); if (!store_ctx) { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error creating X509_STORE_CTX"; + qWarning() << "PaymentRequestPlus::getMerchant : Payment request: error creating X509_STORE_CTX"; return false; } @@ -182,7 +183,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c catch (SSLVerifyError& err) { fResult = false; - qDebug() << "PaymentRequestPlus::getMerchant : SSL error: " << err.what(); + qWarning() << "PaymentRequestPlus::getMerchant : SSL error: " << err.what(); } if (website) diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h index 8c126b1fad..3c4861a4d4 100644 --- a/src/qt/paymentrequestplus.h +++ b/src/qt/paymentrequestplus.h @@ -24,7 +24,7 @@ public: PaymentRequestPlus() { } bool parse(const QByteArray& data); - bool SerializeToString(string* output) const; + bool SerializeToString(std::string* output) const; bool IsInitialized() const; QString getPKIType() const; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 9241f9dc3c..7c0c95c459 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -44,6 +44,7 @@ #include <QUrlQuery> #endif +using namespace std; using namespace boost; const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds @@ -89,7 +90,7 @@ static QList<QString> savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate& cert) { - qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); + qWarning() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); } // @@ -160,7 +161,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) continue; } } - qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; + qWarning() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; // Project for another day: // Fetch certificate revocation lists, and add them to certStore. @@ -194,14 +195,14 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[]) savedPaymentRequests.append(arg); SendCoinsRecipient r; - if (GUIUtil::parseBitcoinURI(arg, &r)) + if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { CBitcoinAddress address(r.address.toStdString()); - SelectParams(CChainParams::MAIN); + SelectParams(CBaseChainParams::MAIN); if (!address.IsValid()) { - SelectParams(CChainParams::TESTNET); + SelectParams(CBaseChainParams::TESTNET); } } } @@ -213,16 +214,20 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[]) if (readPaymentRequest(arg, request)) { if (request.getDetails().network() == "main") - SelectParams(CChainParams::MAIN); - else - SelectParams(CChainParams::TESTNET); + { + SelectParams(CBaseChainParams::MAIN); + } + else if (request.getDetails().network() == "test") + { + SelectParams(CBaseChainParams::TESTNET); + } } } else { // Printing to debug.log is about the best we can do here, the // GUI hasn't started yet so we can't pop up a message box. - qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg; + qWarning() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg; } } return true; @@ -340,20 +345,14 @@ void PaymentServer::initNetManager() QNetworkProxy proxy; - // Query active proxy (fails if no SOCKS5 proxy) + // Query active SOCKS5 proxy if (optionsModel->getProxySettings(proxy)) { - if (proxy.type() == QNetworkProxy::Socks5Proxy) { - netManager->setProxy(proxy); + netManager->setProxy(proxy); - qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port(); - } - else - qDebug() << "PaymentServer::initNetManager : No active proxy server found."; + qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port(); } else - emit message(tr("Net manager warning"), - tr("Your active proxy doesn't support SOCKS5, which is required for payment requests via proxy."), - CClientUIInterface::MSG_WARNING); + qDebug() << "PaymentServer::initNetManager : No active proxy server found."; connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(netRequestFinished(QNetworkReply*))); @@ -402,7 +401,7 @@ void PaymentServer::handleURIOrFile(const QString& s) } else { - qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; + qWarning() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; emit message(tr("URI handling"), tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()), CClientUIInterface::ICON_WARNING); @@ -475,13 +474,13 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl QFile f(filename); if (!f.open(QIODevice::ReadOnly)) { - qDebug() << "PaymentServer::readPaymentRequest : Failed to open " << filename; + qWarning() << "PaymentServer::readPaymentRequest : Failed to open " << filename; return false; } if (f.size() > MAX_PAYMENT_REQUEST_SIZE) { - qDebug() << "PaymentServer::readPaymentRequest : " << filename << " too large"; + qWarning() << "PaymentServer::readPaymentRequest : " << filename << " too large"; return false; } @@ -490,17 +489,6 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl return request.parse(data); } -std::string PaymentServer::mapNetworkIdToName(CChainParams::Network networkId) -{ - if (networkId == CChainParams::MAIN) - return "main"; - if (networkId == CChainParams::TESTNET) - return "test"; - if (networkId == CChainParams::REGTEST) - return "regtest"; - return ""; -} - bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient) { if (!optionsModel) @@ -510,7 +498,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins const payments::PaymentDetails& details = request.getDetails(); // Payment request network matches client network? - if (details.network() != mapNetworkIdToName(Params().NetworkID())) + if (details.network() != Params().NetworkIDString()) { emit message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."), CClientUIInterface::MSG_ERROR); @@ -561,7 +549,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); - if (txOut.IsDust(CTransaction::minRelayTxFee)) { + if (txOut.IsDust(::minRelayTxFee)) { emit message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), CClientUIInterface::MSG_ERROR); @@ -634,7 +622,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien else { // This should never happen, because sending coins should have // just unlocked the wallet and refilled the keypool. - qDebug() << "PaymentServer::fetchPaymentACK : Error getting refund key, refund_to not set"; + qWarning() << "PaymentServer::fetchPaymentACK : Error getting refund key, refund_to not set"; } } @@ -646,7 +634,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien } else { // This should never happen, either. - qDebug() << "PaymentServer::fetchPaymentACK : Error serializing payment message"; + qWarning() << "PaymentServer::fetchPaymentACK : Error serializing payment message"; } } @@ -659,7 +647,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) .arg(reply->request().url().toString()) .arg(reply->errorString()); - qDebug() << "PaymentServer::netRequestFinished : " << msg; + qWarning() << "PaymentServer::netRequestFinished : " << msg; emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); return; } @@ -673,7 +661,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) SendCoinsRecipient recipient; if (!request.parse(data)) { - qDebug() << "PaymentServer::netRequestFinished : Error parsing payment request"; + qWarning() << "PaymentServer::netRequestFinished : Error parsing payment request"; emit message(tr("Payment request error"), tr("Payment request can not be parsed!"), CClientUIInterface::MSG_ERROR); @@ -691,7 +679,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) QString msg = tr("Bad response from server %1") .arg(reply->request().url().toString()); - qDebug() << "PaymentServer::netRequestFinished : " << msg; + qWarning() << "PaymentServer::netRequestFinished : " << msg; emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); } else @@ -707,7 +695,7 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> QString errString; foreach (const QSslError& err, errs) { - qDebug() << "PaymentServer::reportSslErrors : " << err; + qWarning() << "PaymentServer::reportSslErrors : " << err; errString += err.errorString() + "\n"; } emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR); diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index d6949a47ce..d84d09c57d 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -118,7 +118,6 @@ protected: private: static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request); - std::string mapNetworkIdToName(CChainParams::Network networkId); bool processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient); void fetchRequest(const QUrl& url); diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index d8dad15c0d..cc2f00916f 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -22,7 +22,7 @@ #endif #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" /* for USE_QRCODE */ +#include "config/bitcoin-config.h" /* for USE_QRCODE */ #endif #ifdef USE_QRCODE diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 844d62518c..b5a998f9f5 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -21,7 +21,9 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel addNewRequest(request); /* These columns must match the indices in the ColumnIndex enumeration */ - columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount"); + columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle(); + + connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); } RecentRequestsTableModel::~RecentRequestsTableModel() @@ -101,6 +103,24 @@ QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orien return QVariant(); } +/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ +void RecentRequestsTableModel::updateAmountColumnTitle() +{ + columns[Amount] = getAmountTitle(); + emit headerDataChanged(Qt::Horizontal,Amount,Amount); +} + +/** Gets title for amount column including current display unit if optionsModel reference available. */ +QString RecentRequestsTableModel::getAmountTitle() +{ + QString amountTitle = tr("Amount"); + if (this->walletModel->getOptionsModel() != NULL) + { + amountTitle += " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")"; + } + return amountTitle; +} + QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); @@ -185,6 +205,11 @@ void RecentRequestsTableModel::sort(int column, Qt::SortOrder order) emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex())); } +void RecentRequestsTableModel::updateDisplayUnit() +{ + updateAmountColumnTitle(); +} + bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const { RecentRequestEntry *pLeft = &left; diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index d4cc5078aa..4f0b241259 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -91,12 +91,18 @@ public: public slots: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + void updateDisplayUnit(); private: WalletModel *walletModel; QStringList columns; QList<RecentRequestEntry> list; int64_t nReceiveRequestsMaxId; + + /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ + void updateAmountColumnTitle(); + /** Gets title for amount column including current display unit if optionsModel reference available. */ + QString getAmountTitle(); }; #endif diff --git a/src/qt/res/bitcoin-qt-res.rc b/src/qt/res/bitcoin-qt-res.rc index ee23ae9b78..809235be5f 100644 --- a/src/qt/res/bitcoin-qt-res.rc +++ b/src/qt/res/bitcoin-qt-res.rc @@ -8,7 +8,6 @@ IDI_ICON2 ICON DISCARDABLE "icons/bitcoin_testnet.ico" #define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) #define VER_FILEVERSION VER_PRODUCTVERSION #define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR -#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin developers" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/src/qt/res/icons/unit_btc.png b/src/qt/res/icons/unit_btc.png Binary files differnew file mode 100644 index 0000000000..ec3497435c --- /dev/null +++ b/src/qt/res/icons/unit_btc.png diff --git a/src/qt/res/icons/unit_mbtc.png b/src/qt/res/icons/unit_mbtc.png Binary files differnew file mode 100644 index 0000000000..32bf2f2ca0 --- /dev/null +++ b/src/qt/res/icons/unit_mbtc.png diff --git a/src/qt/res/icons/unit_ubtc.png b/src/qt/res/icons/unit_ubtc.png Binary files differnew file mode 100644 index 0000000000..d5a154882b --- /dev/null +++ b/src/qt/res/icons/unit_ubtc.png diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 199050cc57..e1f40ddd09 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -231,6 +231,7 @@ RPCConsole::RPCConsole(QWidget *parent) : startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); + ui->detailWidget->hide(); clear(); @@ -316,7 +317,7 @@ void RPCConsole::setClientModel(ClientModel *model) ui->buildDate->setText(model->formatBuildDate()); ui->startupTime->setText(model->formatClientStartupTime()); - ui->networkName->setText(model->getNetworkName()); + ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); } } @@ -581,7 +582,7 @@ void RPCConsole::peerLayoutChanged() if (fUnselect && selectedRow >= 0) { ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()), - QItemSelectionModel::Deselect); + QItemSelectionModel::Deselect); } if (fReselect) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3fee34d00e..3aeff3eace 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -13,7 +13,6 @@ #include <QDialog> class ClientModel; -class CNodeCombinedStats; QT_BEGIN_NAMESPACE class QItemSelection; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b7d74d7039..6f10ed5b0b 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -90,8 +90,9 @@ void SendCoinsDialog::setModel(WalletModel *model) } } - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); - connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), + model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); // Coin Control @@ -383,10 +384,14 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) return true; } -void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchBalance, qint64 watchUnconfirmedBalance, qint64 watchImmatureBalance) { Q_UNUSED(unconfirmedBalance); Q_UNUSED(immatureBalance); + Q_UNUSED(watchBalance); + Q_UNUSED(watchUnconfirmedBalance); + Q_UNUSED(watchImmatureBalance); if(model && model->getOptionsModel()) { @@ -396,7 +401,7 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint void SendCoinsDialog::updateDisplayUnit() { - setBalance(model->getBalance(), 0, 0); + setBalance(model->getBalance(), 0, 0, 0, 0, 0); } void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index fcae26c720..6cdf4a00c8 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -47,7 +47,8 @@ public slots: void accept(); SendCoinsEntry *addEntry(); void updateTabsAndLabels(); - void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); private: Ui::SendCoinsDialog *ui; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 3e56412c7c..d4d021e21c 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -27,7 +27,6 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : #if QT_VERSION >= 0x040700 ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); - ui->addressIn_VM->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); #endif GUIUtil::setupAddressWidget(ui->addressIn_SM, this); diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 7dee7a9cda..e92a7d2b1a 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -56,6 +56,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig void PaymentServerTests::paymentServerTests() { + SelectParams(CBaseChainParams::MAIN); OptionsModel optionsModel; PaymentServer* server = new PaymentServer(NULL, false); X509_STORE* caStore = X509_STORE_new(); diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 220da28cfe..03a2381c06 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,5 +1,5 @@ #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #ifdef ENABLE_WALLET diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 0cfcb048c8..36e4d50e30 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -12,12 +12,16 @@ #include "main.h" #include "paymentserver.h" #include "transactionrecord.h" +#include "timedata.h" #include "ui_interface.h" #include "wallet.h" +#include "script.h" #include <stdint.h> #include <string> +using namespace std; + QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { AssertLockHeld(cs_main); @@ -51,8 +55,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; int64_t nTime = wtx.GetTxTime(); - int64_t nCredit = wtx.GetCredit(); - int64_t nDebit = wtx.GetDebit(); + int64_t nCredit = wtx.GetCredit(ISMINE_ALL); + int64_t nDebit = wtx.GetDebit(ISMINE_ALL); int64_t nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); @@ -94,10 +98,11 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("To") + ":</b> "; strHTML += GUIUtil::HtmlEscape(rec->address); + QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only"); if (!wallet->mapAddressBook[address].name.empty()) - strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; + strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; else - strHTML += " (" + tr("own address") + ")"; + strHTML += " (" + addressOwned + ")"; strHTML += "<br>"; } } @@ -128,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // int64_t nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += wallet->GetCredit(txout); + nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); strHTML += "<b>" + tr("Credit") + ":</b> "; if (wtx.IsInMainChain()) strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; @@ -145,22 +150,33 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco } else { - bool fAllFromMe = true; + isminetype fAllFromMe = ISMINE_SPENDABLE; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && wallet->IsMine(txin); + { + isminetype mine = wallet->IsMine(txin); + if(fAllFromMe > mine) fAllFromMe = mine; + } - bool fAllToMe = true; + isminetype fAllToMe = ISMINE_SPENDABLE; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && wallet->IsMine(txout); + { + isminetype mine = wallet->IsMine(txout); + if(fAllToMe > mine) fAllToMe = mine; + } if (fAllFromMe) { + if(fAllFromMe == ISMINE_WATCH_ONLY) + strHTML += "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>"; + // // Debit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if (wallet->IsMine(txout)) + // Ignore change + isminetype toSelf = wallet->IsMine(txout); + if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE)) continue; if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) @@ -173,11 +189,17 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + if(toSelf == ISMINE_SPENDABLE) + strHTML += " (own address)"; + else if(toSelf == ISMINE_WATCH_ONLY) + strHTML += " (watch-only)"; strHTML += "<br>"; } } strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>"; + if(toSelf) + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>"; } if (fAllToMe) @@ -185,8 +207,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Payment to self int64_t nChange = wtx.GetChange(); int64_t nValue = nCredit - nChange; - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; + strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; + strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; } int64_t nTxFee = nDebit - wtx.GetValueOut(); @@ -200,10 +222,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; } } @@ -253,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); @@ -283,7 +305,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue); - strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "</li>"; + strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; + strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>"; } } } diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index f9546fddb5..7293029787 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -24,7 +24,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : typeFilter(ALL_TYPES), minAmount(0), limitRows(-1), - showInactive(true) + showInactive(false) { } @@ -39,7 +39,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); - if(!showInactive && status == TransactionStatus::Conflicted) + if(!showInactive && status == TransactionStatus::Conflicted && type == TransactionRecord::Other) return false; if(!(TYPE(type) & typeFilter)) return false; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 5a3728f498..7d29c212b3 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -5,6 +5,7 @@ #include "transactionrecord.h" #include "base58.h" +#include "timedata.h" #include "wallet.h" #include <stdint.h> @@ -32,7 +33,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); int64_t nCredit = wtx.GetCredit(true); - int64_t nDebit = wtx.GetDebit(); + int64_t nDebit = wtx.GetDebit(ISMINE_ALL); int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; @@ -44,12 +45,14 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if(wallet->IsMine(txout)) + isminetype mine = wallet->IsMine(txout); + if(mine) { TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; + sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Bitcoin Address @@ -74,13 +77,22 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * } else { - bool fAllFromMe = true; + bool involvesWatchAddress = false; + isminetype fAllFromMe = ISMINE_SPENDABLE; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && wallet->IsMine(txin); + { + isminetype mine = wallet->IsMine(txin); + if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true; + if(fAllFromMe > mine) fAllFromMe = mine; + } - bool fAllToMe = true; + isminetype fAllToMe = ISMINE_SPENDABLE; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && wallet->IsMine(txout); + { + isminetype mine = wallet->IsMine(txout); + if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true; + if(fAllToMe > mine) fAllToMe = mine; + } if (fAllFromMe && fAllToMe) { @@ -89,6 +101,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); + parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument } else if (fAllFromMe) { @@ -102,6 +115,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * const CTxOut& txout = wtx.vout[nOut]; TransactionRecord sub(hash, nTime); sub.idx = parts.size(); + sub.involvesWatchAddress = involvesWatchAddress; if(wallet->IsMine(txout)) { @@ -142,6 +156,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * // Mixed debit transaction, can't break down payees // parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); + parts.last().involvesWatchAddress = involvesWatchAddress; } } @@ -169,6 +184,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); + status.hasConflicting = false; + if (!IsFinalTx(wtx, chainActive.Height() + 1)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) @@ -212,6 +229,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (status.depth < 0) { status.status = TransactionStatus::Conflicted; + status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { @@ -220,6 +238,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; + status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (status.depth < RecommendedNumConfirmations) { @@ -230,13 +249,13 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.status = TransactionStatus::Confirmed; } } - } -bool TransactionRecord::statusUpdateNeeded() +bool TransactionRecord::statusUpdateNeeded(int64_t nConflictsReceived) { AssertLockHeld(cs_main); - return status.cur_num_blocks != chainActive.Height(); + return (status.cur_num_blocks != chainActive.Height() || + status.cur_num_conflicts != nConflictsReceived); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index af6fd403b3..d3cfa77d97 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -19,9 +19,17 @@ class TransactionStatus { public: TransactionStatus(): - countsForBalance(false), sortKey(""), - matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) - { } + countsForBalance(false), + sortKey(""), + matures_in(0), + status(Offline), + hasConflicting(false), + depth(0), + open_for(0), + cur_num_blocks(-1), + cur_num_conflicts(-1) + { + } enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ @@ -51,6 +59,10 @@ public: /** @name Reported status @{*/ Status status; + + // Has conflicting transactions spending same prevout + bool hasConflicting; + qint64 depth; qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before @@ -59,6 +71,10 @@ public: /** Current number of blocks (to know whether cached status is still valid) */ int cur_num_blocks; + + /** Number of conflicts received into wallet as of last status update */ + int64_t cur_num_conflicts; + }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has @@ -121,6 +137,9 @@ public: /** Status: can change with block chain update */ TransactionStatus status; + /** Whether the transaction was sent/received with a watch-only address */ + bool involvesWatchAddress; + /** Return the unique identifier for this transaction (part) */ QString getTxID() const; @@ -133,7 +152,7 @@ public: /** Return whether a status update is needed. */ - bool statusUpdateNeeded(); + bool statusUpdateNeeded(int64_t nConflictsReceived); }; #endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index b9fcd0d6b0..3605cc1bad 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -130,12 +130,12 @@ public: case CT_NEW: if(inModel) { - qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model"; break; } if(!inWallet) { - qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet"; + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet"; break; } if(showTransaction) @@ -159,7 +159,7 @@ public: case CT_DELETED: if(!inModel) { - qDebug() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; + qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model"; break; } // Removed -- remove entire transaction from table @@ -168,8 +168,7 @@ public: parent->endRemoveRows(); break; case CT_UPDATED: - // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for - // visible transactions. + emit parent->dataChanged(parent->index(lowerIndex, parent->Status), parent->index(upperIndex-1, parent->Amount)); break; } } @@ -190,20 +189,21 @@ public: // stuck if the core is holding the locks for a longer time - for // example, during a wallet rescan. // - // If a status update is needed (blocks came in since last check), - // update the status of this transaction from the wallet. Otherwise, + // If a status update is needed (blocks or conflicts came in since last check), + // update the status of this transaction from the wallet. Otherwise, // simply re-use the cached status. TRY_LOCK(cs_main, lockMain); if(lockMain) { TRY_LOCK(wallet->cs_wallet, lockWallet); - if(lockWallet && rec->statusUpdateNeeded()) + if(lockWallet && rec->statusUpdateNeeded(wallet->nConflictsReceived)) { std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { rec->updateStatus(mi->second); + rec->status.cur_num_conflicts = wallet->nConflictsReceived; } } } @@ -235,8 +235,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren walletModel(parent), priv(new TransactionTablePriv(wallet, this)) { - columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); - + columns << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -247,6 +246,13 @@ TransactionTableModel::~TransactionTableModel() delete priv; } +/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ +void TransactionTableModel::updateAmountColumnTitle() +{ + columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + emit headerDataChanged(Qt::Horizontal,Amount,Amount); +} + void TransactionTableModel::updateTransaction(const QString &hash, int status) { uint256 updated; @@ -363,6 +369,8 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Payment to yourself"); case TransactionRecord::Generated: return tr("Mined"); + case TransactionRecord::Other: + return tr("Other"); default: return QString(); } @@ -388,19 +396,22 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { + // mark transactions involving watch-only addresses: + QString watchAddress = wtx->involvesWatchAddress ? " (w) " : ""; + switch(wtx->type) { case TransactionRecord::RecvFromOther: - return QString::fromStdString(wtx->address); + return QString::fromStdString(wtx->address) + watchAddress; case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: - return lookupAddress(wtx->address, tooltip); + return lookupAddress(wtx->address, tooltip) + watchAddress; case TransactionRecord::SendToOther: - return QString::fromStdString(wtx->address); + return QString::fromStdString(wtx->address) + watchAddress; case TransactionRecord::SendToSelf: default: - return tr("(n/a)"); + return tr("(n/a)") + watchAddress; } } @@ -535,7 +546,13 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTooltip(rec); case Qt::TextAlignmentRole: return column_alignments[index.column()]; + case Qt::BackgroundColorRole: + if (rec->status.hasConflicting) + return COLOR_HASCONFLICTING_BG; + break; case Qt::ForegroundRole: + if (rec->status.hasConflicting) + return COLOR_HASCONFLICTING; // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { @@ -624,5 +641,6 @@ QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex void TransactionTableModel::updateDisplayUnit() { // emit dataChanged to update Amount column with the current unit + updateAmountColumnTitle(); emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 333e6bc6ed..e8b6ed065d 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -87,6 +87,8 @@ public slots: void updateTransaction(const QString &hash, int status); void updateConfirmations(); void updateDisplayUnit(); + /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */ + void updateAmountColumnTitle(); friend class TransactionTablePriv; }; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index d4d29416ca..d6d210a561 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -309,7 +309,7 @@ void TransactionView::exportClicked() writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole); - writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); + writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole); writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); if(!writer.write()) { diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index eb647d0170..5fb0da145d 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -117,6 +117,7 @@ void ShutdownWindow::showShutdownWindow(BitcoinGUI *window) tr("Bitcoin Core is shutting down...") + "<br /><br />" + tr("Do not shut down the computer until this window disappears."))); shutdownWindow->setLayout(layout); + shutdownWindow->setWindowTitle(window->windowTitle()); // Center shutdown window at where main window was const QPoint global = window->mapToGlobal(window->rect().center()); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 87ff3db584..7317c32766 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -24,6 +24,8 @@ #include <QSet> #include <QTimer> +using namespace std; + WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), @@ -58,7 +60,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const std::vector<COutput> vCoins; wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) - nBalance += out.tx->vout[out.i].nValue; + if(out.fSpendable) + nBalance += out.tx->vout[out.i].nValue; return nBalance; } @@ -76,6 +79,21 @@ qint64 WalletModel::getImmatureBalance() const return wallet->GetImmatureBalance(); } +qint64 WalletModel::getWatchBalance() const +{ + return wallet->GetWatchOnlyBalance(); +} + +qint64 WalletModel::getWatchUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedWatchOnlyBalance(); +} + +qint64 WalletModel::getWatchImmatureBalance() const +{ + return wallet->GetImmatureWatchOnlyBalance(); +} + int WalletModel::getNumTransactions() const { int numTransactions = 0; @@ -124,18 +142,34 @@ void WalletModel::checkBalanceChanged() qint64 newBalance = getBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newImmatureBalance = getImmatureBalance(); + qint64 newWatchOnlyBalance = getWatchBalance(); + qint64 newWatchUnconfBalance = getWatchUnconfirmedBalance(); + qint64 newWatchImmatureBalance = getWatchImmatureBalance(); - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || + cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) { cachedBalance = newBalance; cachedUnconfirmedBalance = newUnconfirmedBalance; cachedImmatureBalance = newImmatureBalance; - emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance); + cachedWatchOnlyBalance = newWatchOnlyBalance; + cachedWatchUnconfBalance = newWatchUnconfBalance; + cachedWatchImmatureBalance = newWatchImmatureBalance; + emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, + newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance); } } void WalletModel::updateTransaction(const QString &hash, int status) { + if (status == CT_GOT_CONFLICT) + { + emit message(tr("Conflict Received"), + tr("WARNING: Transaction may never be confirmed. Its input was seen being spent by another transaction on the network. Wait for confirmation!"), + CClientUIInterface::MSG_WARNING); + return; + } + if(transactionTableModel) transactionTableModel->updateTransaction(hash, status); @@ -533,7 +567,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); vOutputs.push_back(out); } } @@ -560,7 +594,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); vCoins.push_back(out); } @@ -571,11 +605,12 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0); + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true); } CTxDestination address; - if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue; + if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) + continue; mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out); } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ccf590aaed..7ad54ff8e6 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -128,6 +128,9 @@ public: qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; qint64 getImmatureBalance() const; + qint64 getWatchBalance() const; + qint64 getWatchUnconfirmedBalance() const; + qint64 getWatchImmatureBalance() const; int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; @@ -206,6 +209,9 @@ private: qint64 cachedBalance; qint64 cachedUnconfirmedBalance; qint64 cachedImmatureBalance; + qint64 cachedWatchOnlyBalance; + qint64 cachedWatchUnconfBalance; + qint64 cachedWatchImmatureBalance; qint64 cachedNumTransactions; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -218,7 +224,8 @@ private: signals: // Signal that balance in wallet changed - void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); // Number of transactions in wallet changed void numTransactionsChanged(int count); diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp index b7526f0ae4..a06f42f66e 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -6,11 +6,14 @@ #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 #include "init.h" +#include "util.h" #include <windows.h> #include <QDebug> +#include <openssl/rand.h> + // If we don't want a message to be processed by Qt, return true and set result to // the value that the window procedure should return. Otherwise return false. bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult) @@ -19,6 +22,16 @@ bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pM MSG *pMsg = static_cast<MSG *>(pMessage); + // Seed OpenSSL PRNG with Windows event data (e.g. mouse movements and other user interactions) + if (RAND_event(pMsg->message, pMsg->wParam, pMsg->lParam) == 0) { + // Warn only once as this is performance-critical + static bool warned = false; + if (!warned) { + LogPrint("%s: OpenSSL RAND_event() failed to seed OpenSSL PRNG with enough data.\n", __func__); + warned = true; + } + } + switch(pMsg->message) { case WM_QUERYENDSESSION: @@ -45,13 +58,13 @@ void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, c typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR); PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate"); if (shutdownBRCreate == NULL) { - qDebug() << "registerShutdownBlockReason : GetProcAddress for ShutdownBlockReasonCreate failed"; + qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed"; return; } if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str())) - qDebug() << "registerShutdownBlockReason : Successfully registered: " + strReason; + qWarning() << "registerShutdownBlockReason: Successfully registered: " + strReason; else - qDebug() << "registerShutdownBlockReason : Failed to register: " + strReason; + qWarning() << "registerShutdownBlockReason: Failed to register: " + strReason; } #endif diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 2a21fb462a..a67f266a13 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -66,7 +66,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) result.push_back(Pair("tx", txs)); result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("nonce", (uint64_t)block.nNonce)); - result.push_back(Pair("bits", HexBits(block.nBits))); + result.push_back(Pair("bits", strprintf("%08x", block.nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); @@ -438,7 +438,7 @@ Value getblockchaininfo(const Array& params, bool fHelp) "Returns an object containing various state info regarding block chain processing.\n" "\nResult:\n" "{\n" - " \"chain\": \"xxxx\", (string) current chain (main, testnet3, regtest)\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" @@ -451,14 +451,11 @@ Value getblockchaininfo(const Array& params, bool fHelp) ); Object obj; - std::string chain = Params().DataDir(); - if(chain.empty()) - chain = "main"; - obj.push_back(Pair("chain", chain)); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip()))); - obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip()))); + obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); return obj; } diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index b89a95ad11..5edeecf933 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <set> #include "rpcclient.h" #include "rpcprotocol.h" @@ -12,271 +13,128 @@ #include <stdint.h> -#include <boost/algorithm/string.hpp> -#include <boost/asio.hpp> -#include <boost/asio/ssl.hpp> -#include <boost/bind.hpp> -#include <boost/filesystem.hpp> -#include <boost/foreach.hpp> -#include <boost/iostreams/concepts.hpp> -#include <boost/iostreams/stream.hpp> -#include <boost/shared_ptr.hpp> -#include "json/json_spirit_writer_template.h" - using namespace std; -using namespace boost; -using namespace boost::asio; using namespace json_spirit; -Object CallRPC(const string& strMethod, const Array& params) +class CRPCConvertParam { - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword=<password> in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().string().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl", false); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); - SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); - - bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started - do { - bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))); - if (fConnected) break; - if (fWait) - MilliSleep(1000); - else - throw runtime_error("couldn't connect to server"); - } while (fWait); - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map<string, string> mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; +public: + std::string methodName; // method whose params want conversion + int paramIdx; // 0-based idx of param to convert +}; - // Receive HTTP reply status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Receive HTTP reply message headers and body - map<string, string> mapHeaders; - string strReply; - ReadHTTPMessage(stream, mapHeaders, strReply, nProto); - - if (nStatus == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); +static const CRPCConvertParam vRPCConvertParams[] = +{ + { "stop", 0 }, + { "getaddednodeinfo", 0 }, + { "setgenerate", 0 }, + { "setgenerate", 1 }, + { "getnetworkhashps", 0 }, + { "getnetworkhashps", 1 }, + { "sendtoaddress", 1 }, + { "settxfee", 0 }, + { "getreceivedbyaddress", 1 }, + { "getreceivedbyaccount", 1 }, + { "listreceivedbyaddress", 0 }, + { "listreceivedbyaddress", 1 }, + { "listreceivedbyaddress", 2 }, + { "listreceivedbyaccount", 0 }, + { "listreceivedbyaccount", 1 }, + { "listreceivedbyaccount", 2 }, + { "getbalance", 1 }, + { "getbalance", 2 }, + { "getblockhash", 0 }, + { "move", 2 }, + { "move", 3 }, + { "sendfrom", 2 }, + { "sendfrom", 3 }, + { "listtransactions", 1 }, + { "listtransactions", 2 }, + { "listtransactions", 3 }, + { "listaccounts", 0 }, + { "listaccounts", 1 }, + { "walletpassphrase", 1 }, + { "getblocktemplate", 0 }, + { "listsinceblock", 1 }, + { "listsinceblock", 2 }, + { "sendmany", 1 }, + { "sendmany", 2 }, + { "addmultisigaddress", 0 }, + { "addmultisigaddress", 1 }, + { "createmultisig", 0 }, + { "createmultisig", 1 }, + { "listunspent", 0 }, + { "listunspent", 1 }, + { "listunspent", 2 }, + { "getblock", 1 }, + { "getrawtransaction", 1 }, + { "createrawtransaction", 0 }, + { "createrawtransaction", 1 }, + { "signrawtransaction", 1 }, + { "signrawtransaction", 2 }, + { "sendrawtransaction", 1 }, + { "gettxout", 1 }, + { "gettxout", 2 }, + { "lockunspent", 0 }, + { "lockunspent", 1 }, + { "importprivkey", 2 }, + { "importaddress", 2 }, + { "verifychain", 0 }, + { "verifychain", 1 }, + { "keypoolrefill", 0 }, + { "getrawmempool", 0 }, + { "estimatefee", 0 }, + { "estimatepriority", 0 }, +}; + +class CRPCConvertTable +{ +private: + std::set<std::pair<std::string, int> > members; - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); +public: + CRPCConvertTable(); - return reply; -} + bool convert(const std::string& method, int idx) { + return (members.count(std::make_pair(method, idx)) > 0); + } +}; -template<typename T> -void ConvertTo(Value& value, bool fAllowNull=false) +CRPCConvertTable::CRPCConvertTable() { - if (fAllowNull && value.type() == null_type) - return; - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - string strJSON = value.get_str(); - if (!read_string(strJSON, value2)) - throw runtime_error(string("Error parsing JSON:")+strJSON); - ConvertTo<T>(value2, fAllowNull); - value = value2; - } - else - { - value = value.get_value<T>(); + const unsigned int n_elem = + (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); + + for (unsigned int i = 0; i < n_elem; i++) { + members.insert(std::make_pair(vRPCConvertParams[i].methodName, + vRPCConvertParams[i].paramIdx)); } } +static CRPCConvertTable rpcCvtTable; + // Convert strings to command-specific RPC representation Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) { Array params; - BOOST_FOREACH(const std::string ¶m, strParams) - params.push_back(param); - - int n = params.size(); - // - // Special case non-string parameter types - // - if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]); - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "getbalance" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "getblockhash" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]); - if (strMethod == "move" && n > 3) ConvertTo<int64_t>(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo<int64_t>(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo<int64_t>(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "walletpassphrase" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]); - if (strMethod == "listsinceblock" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]); - if (strMethod == "sendmany" && n > 2) ConvertTo<int64_t>(params[2]); - if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "createmultisig" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "listunspent" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "listunspent" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); - if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "getrawtransaction" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]); - if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); - if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); - if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); - if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true); - if (strMethod == "gettxout" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); - if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]); - if (strMethod == "verifychain" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "verifychain" && n > 1) ConvertTo<int64_t>(params[1]); - if (strMethod == "keypoolrefill" && n > 0) ConvertTo<int64_t>(params[0]); - if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "estimatefee" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "estimatepriority" && n > 0) ConvertTo<boost::int64_t>(params[0]); + for (unsigned int idx = 0; idx < strParams.size(); idx++) { + const std::string& strVal = strParams[idx]; - return params; -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; + // insert string value directly + if (!rpcCvtTable.convert(strMethod, idx)) { + params.push_back(strVal); } - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - std::vector<std::string> strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); + // parse string as JSON, insert bool/number/object/etc. value + else { + Value jVal; + if (!read_string(strVal, jVal)) + throw runtime_error(string("Error parsing JSON:")+strVal); + params.push_back(jVal); } } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - strPrint = string("error: ") + e.what(); - nRet = abs(RPC_MISC_ERROR); - } - catch (...) { - PrintExceptionContinue(NULL, "CommandLineRPC()"); - throw; - } - if (strPrint != "") - { - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); - } - return nRet; -} - -std::string HelpMessageCli(bool mainProgram) -{ - string strUsage; - if(mainProgram) - { - strUsage += _("Options:") + "\n"; - strUsage += " -? " + _("This help message") + "\n"; - strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; - strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; - strUsage += " -testnet " + _("Use the test network") + "\n"; - strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " - "solved instantly. This is intended for regression testing tools and app development.") + "\n"; - } else { - strUsage += _("RPC client options:") + "\n"; - } - - strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; - strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; - strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; - - if(mainProgram) - { - strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; - strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; - - strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; - strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; - } - - return strUsage; + return params; } diff --git a/src/rpcclient.h b/src/rpcclient.h index e101d22ec5..840890e34b 100644 --- a/src/rpcclient.h +++ b/src/rpcclient.h @@ -10,16 +10,6 @@ #include "json/json_spirit_utils.h" #include "json/json_spirit_writer_template.h" -int CommandLineRPC(int argc, char *argv[]); - json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams); -/** Show help message for bitcoin-cli. - * The mainProgram argument is used to determine whether to show this message as main program - * (and include some common options) or as sub-header of another help message. - * - * @note the argument can be removed once bitcoin-cli functionality is removed from bitcoind - */ -std::string HelpMessageCli(bool mainProgram); - #endif diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 593e0d2b6b..4193f41b49 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -133,6 +133,60 @@ Value importprivkey(const Array& params, bool fHelp) return Value::null; } +Value importaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importaddress <address> [label] [rescan=true]\n" + "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend."); + + CScript script; + + CBitcoinAddress address(params[0].get_str()); + if (address.IsValid()) { + script.SetDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector<unsigned char> data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); + } + + 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(); + + { + LOCK2(cs_main, pwalletMain->cs_wallet); + + // 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 Value::null; + + pwalletMain->MarkDirty(); + + if (!pwalletMain->AddWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return Value::null; +} + Value importwallet(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -157,7 +211,7 @@ Value importwallet(const Array& params, bool fHelp) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - int64_t nTimeBegin = chainActive.Tip()->nTime; + int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); bool fGood = true; @@ -215,7 +269,7 @@ Value importwallet(const Array& params, bool fHelp) pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI CBlockIndex *pindex = chainActive.Tip(); - while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) + while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) pindex = pindex->pprev; if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey) @@ -301,7 +355,7 @@ Value dumpwallet(const Array& params, bool fHelp) file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); - file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime)); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); file << "\n"; for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 3caf7d89fe..c7621dc137 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -9,6 +9,7 @@ #include "net.h" #include "main.h" #include "miner.h" +#include "pow.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" @@ -24,36 +25,6 @@ using namespace json_spirit; using namespace std; -#ifdef ENABLE_WALLET -// Key used by getwork miners. -// Allocated in InitRPCMining, free'd in ShutdownRPCMining -static CReserveKey* pMiningKey = NULL; - -void InitRPCMining() -{ - if (!pwalletMain) - return; - - // getwork/getblocktemplate mining rewards paid here: - pMiningKey = new CReserveKey(pwalletMain); -} - -void ShutdownRPCMining() -{ - if (!pMiningKey) - return; - - delete pMiningKey; pMiningKey = NULL; -} -#else -void InitRPCMining() -{ -} -void ShutdownRPCMining() -{ -} -#endif - // Return average network hashes per second based on the last 'lookup' blocks, // or from the last difficulty change if 'lookup' is nonpositive. // If 'height' is nonnegative, compute the estimate at the time when a given block was found. @@ -131,9 +102,6 @@ Value getgenerate(const Array& params, bool fHelp) + HelpExampleRpc("getgenerate", "") ); - if (!pMiningKey) - return false; - return GetBoolArg("-gen", false); } @@ -253,6 +221,7 @@ Value getmininginfo(const Array& params, bool fHelp) " \"hashespersec\": n (numeric) The hashes per second of the generation, or 0 if no generation.\n" " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -268,7 +237,8 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().RPCisTestNet())); + obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); + obj.push_back(Pair("chain", Params().NetworkIDString())); #ifdef ENABLE_WALLET obj.push_back(Pair("generate", getgenerate(params, false))); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); @@ -277,131 +247,19 @@ Value getmininginfo(const Array& params, bool fHelp) } -#ifdef ENABLE_WALLET -Value getwork(const Array& params, bool fHelp) +Value prioritisetransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() != 3) throw runtime_error( - "getwork ( \"data\" )\n" - "\nIf 'data' is not specified, it returns the formatted hash data to work on.\n" - "If 'data' is specified, tries to solve the block and returns true if it was successful.\n" - "\nArguments:\n" - "1. \"data\" (string, optional) The hex encoded data to solve\n" - "\nResult (when 'data' is not specified):\n" - "{\n" - " \"midstate\" : \"xxxx\", (string) The precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated - " \"data\" : \"xxxxx\", (string) The block data\n" - " \"hash1\" : \"xxxxx\", (string) The formatted hash buffer for second hash (DEPRECATED)\n" // deprecated - " \"target\" : \"xxxx\" (string) The little endian hash target\n" - "}\n" - "\nResult (when 'data' is specified):\n" - "true|false (boolean) If solving the block specified in the 'data' was successfull\n" - "\nExamples:\n" - + HelpExampleCli("getwork", "") - + HelpExampleRpc("getwork", "") - ); - - if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); - - typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t; - static mapNewBlock_t mapNewBlock; // FIXME: thread safety - static vector<CBlockTemplate*> vNewBlockTemplate; - - if (params.size() == 0) - { - // Update block - static unsigned int nTransactionsUpdatedLast; - static CBlockIndex* pindexPrev; - static int64_t nStart; - static CBlockTemplate* pblocktemplate; - if (pindexPrev != chainActive.Tip() || - (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) - { - if (pindexPrev != chainActive.Tip()) - { - // Deallocate old blocks since they're obsolete now - mapNewBlock.clear(); - BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) - delete pblocktemplate; - vNewBlockTemplate.clear(); - } - - // Clear pindexPrev so future getworks make a new block, despite any failures from here on - pindexPrev = NULL; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrevNew = chainActive.Tip(); - nStart = GetTime(); - - // Create new block - pblocktemplate = CreateNewBlockWithKey(*pMiningKey); - if (!pblocktemplate) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - vNewBlockTemplate.push_back(pblocktemplate); + "prioritisetransaction <txid> <priority delta> <fee delta>\n" + "Accepts the transaction into mined blocks at a higher (or lower) priority"); - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - CBlock* pblock = &pblocktemplate->block; // pointer for convenience - - // Update nTime - UpdateTime(*pblock, pindexPrev); - pblock->nNonce = 0; - - // Update nExtraNonce - static unsigned int nExtraNonce = 0; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - - // Save - mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); - - // Pre-build hash buffers - char pmidstate[32]; - char pdata[128]; - char phash1[64]; - FormatHashBuffers(pblock, pmidstate, pdata, phash1); - - uint256 hashTarget = uint256().SetCompact(pblock->nBits); - - Object result; - result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated - result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); - result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated - result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); - return result; - } - else - { - // Parse parameters - vector<unsigned char> vchData = ParseHex(params[0].get_str()); - if (vchData.size() != 128) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); - CBlock* pdata = (CBlock*)&vchData[0]; - - // Byte reverse - for (int i = 0; i < 128/4; i++) - ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); - - // Get saved block - if (!mapNewBlock.count(pdata->hashMerkleRoot)) - return false; - CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; - - pblock->nTime = pdata->nTime; - pblock->nNonce = pdata->nNonce; - pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - - assert(pwalletMain != NULL); - return CheckWork(pblock, *pwalletMain, *pMiningKey); - } + uint256 hash; + hash.SetHex(params[0].get_str()); + mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), params[2].get_int64()); + return true; } -#endif + Value getblocktemplate(const Array& params, bool fHelp) { @@ -584,8 +442,8 @@ Value getblocktemplate(const Array& params, bool fHelp) result.push_back(Pair("noncerange", "00000000ffffffff")); result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); - result.push_back(Pair("curtime", (int64_t)pblock->nTime)); - result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("curtime", pblock->GetBlockTime())); + result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); return result; diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 77e0e09ec3..cff795bdf4 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -9,6 +9,7 @@ #include "net.h" #include "netbase.h" #include "rpcserver.h" +#include "timedata.h" #include "util.h" #ifdef ENABLE_WALLET #include "wallet.h" @@ -21,10 +22,10 @@ #include "json/json_spirit_utils.h" #include "json/json_spirit_value.h" -using namespace std; using namespace boost; using namespace boost::assign; using namespace json_spirit; +using namespace std; Value getinfo(const Array& params, bool fHelp) { @@ -71,9 +72,9 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); + obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().RPCisTestNet())); + obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); @@ -83,7 +84,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); #endif - obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::minRelayTxFee.GetFeePerK()))); + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } @@ -91,36 +92,45 @@ Value getinfo(const Array& params, bool fHelp) #ifdef ENABLE_WALLET class DescribeAddressVisitor : public boost::static_visitor<Object> { +private: + isminetype mine; + public: + DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} + Object operator()(const CNoDestination &dest) const { return Object(); } Object operator()(const CKeyID &keyID) const { Object obj; CPubKey vchPubKey; - pwalletMain->GetPubKey(keyID, vchPubKey); obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + if (mine == ISMINE_SPENDABLE) { + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + } return obj; } Object operator()(const CScriptID &scriptID) const { Object obj; obj.push_back(Pair("isscript", true)); - CScript subscript; - pwalletMain->GetCScript(scriptID, subscript); - std::vector<CTxDestination> addresses; - txnouttype whichType; - int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.push_back(Pair("script", GetTxnOutputType(whichType))); - obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); - Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); - obj.push_back(Pair("addresses", a)); - if (whichType == TX_MULTISIG) - obj.push_back(Pair("sigsrequired", nRequired)); + if (mine != ISMINE_NO) { + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector<CTxDestination> addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + } return obj; } }; @@ -160,10 +170,11 @@ Value validateaddress(const Array& params, bool fHelp) string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); #ifdef ENABLE_WALLET - bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; - ret.push_back(Pair("ismine", fMine)); - if (fMine) { - Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); + if (mine != ISMINE_NO) { + ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); + Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); ret.insert(ret.end(), detail.begin(), detail.end()); } if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 0eca55a472..cf2c293caf 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -9,6 +9,7 @@ #include "netbase.h" #include "protocol.h" #include "sync.h" +#include "timedata.h" #include "util.h" #include <boost/foreach.hpp> @@ -80,7 +81,7 @@ Value getpeerinfo(const Array& params, bool fHelp) " {\n" " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" " \"addrlocal\":\"ip:port\", (string) local address\n" - " \"services\":\"00000001\", (string) The services\n" + " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" " \"bytessent\": n, (numeric) The total bytes sent\n" @@ -115,7 +116,7 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("addr", stats.addrName)); if (!(stats.addrLocal.empty())) obj.push_back(Pair("addrlocal", stats.addrLocal)); - obj.push_back(Pair("services", strprintf("%08x", stats.nServices))); + obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); obj.push_back(Pair("lastsend", stats.nLastSend)); obj.push_back(Pair("lastrecv", stats.nLastRecv)); obj.push_back(Pair("bytessent", stats.nSendBytes)); @@ -133,6 +134,7 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("startingheight", stats.nStartingHeight)); if (fStateStats) { obj.push_back(Pair("banscore", statestats.nMisbehavior)); + obj.push_back(Pair("syncheight", statestats.nSyncHeight)); } obj.push_back(Pair("syncnode", stats.fSyncNode)); @@ -344,6 +346,7 @@ Value getnetworkinfo(const Array& params, bool fHelp) "{\n" " \"version\": xxxxx, (numeric) the server version\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" " \"timeoffset\": xxxxx, (numeric) the time offset\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" @@ -365,10 +368,11 @@ Value getnetworkinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("version", (int)CLIENT_VERSION)); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); - obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::minRelayTxFee.GetFeePerK()))); + obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string()))); + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); Array localAddresses; { LOCK(cs_mapLocalHost); diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 2718f81783..9e18ca847e 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -25,6 +25,9 @@ using namespace boost; using namespace boost::asio; using namespace json_spirit; +// Number of bytes to allocate and read at most at once in post data +const size_t POST_READ_SIZE = 256 * 1024; + // // HTTP protocol // @@ -54,7 +57,19 @@ static string rfc1123Time() return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); } -string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +static const char *httpStatusDescription(int nStatus) +{ + switch (nStatus) { + case HTTP_OK: return "OK"; + case HTTP_BAD_REQUEST: return "Bad Request"; + case HTTP_FORBIDDEN: return "Forbidden"; + case HTTP_NOT_FOUND: return "Not Found"; + case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; + default: return ""; + } +} + +string HTTPError(int nStatus, bool keepalive, bool headersOnly) { if (nStatus == HTTP_UNAUTHORIZED) return strprintf("HTTP/1.0 401 Authorization Required\r\n" @@ -73,29 +88,32 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive) "</HEAD>\r\n" "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n" "</HTML>\r\n", rfc1123Time(), FormatFullVersion()); - const char *cStatus; - if (nStatus == HTTP_OK) cStatus = "OK"; - else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; - else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; - else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; - else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; - else cStatus = ""; + + return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive, + headersOnly, "text/plain"); +} + +string HTTPReply(int nStatus, const string& strMsg, bool keepalive, + bool headersOnly, const char *contentType) +{ return strprintf( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Connection: %s\r\n" "Content-Length: %u\r\n" - "Content-Type: application/json\r\n" + "Content-Type: %s\r\n" "Server: bitcoin-json-rpc/%s\r\n" "\r\n" "%s", nStatus, - cStatus, + httpStatusDescription(nStatus), rfc1123Time(), keepalive ? "keep-alive" : "close", - strMsg.size(), + (headersOnly ? 0 : strMsg.size()), + contentType, FormatFullVersion(), - strMsg); + (headersOnly ? "" : strMsg.c_str()) + ); } bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, @@ -189,8 +207,17 @@ int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, // Read message if (nLen > 0) { - vector<char> vch(nLen); - stream.read(&vch[0], nLen); + vector<char> vch; + size_t ptr = 0; + while (ptr < (size_t)nLen) + { + size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE); + vch.resize(ptr + bytes_to_read); + stream.read(&vch[ptr], bytes_to_read); + if (!stream) // Connection lost while reading + return HTTP_INTERNAL_SERVER_ERROR; + ptr += bytes_to_read; + } strMessageRet = string(vch.begin(), vch.end()); } diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h index 11bdd171d9..5627077bfb 100644 --- a/src/rpcprotocol.h +++ b/src/rpcprotocol.h @@ -141,7 +141,11 @@ private: }; std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders); -std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive); +std::string HTTPError(int nStatus, bool keepalive, + bool headerOnly = false); +std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive, + bool headerOnly = false, + const char *contentType = "application/json"); bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, std::string& http_method, std::string& http_uri); int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index dee7daeb2a..2306b1b883 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -99,8 +99,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if (chainActive.Contains(pindex)) { entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); - entry.push_back(Pair("time", (int64_t)pindex->nTime)); - entry.push_back(Pair("blocktime", (int64_t)pindex->nTime)); + entry.push_back(Pair("time", pindex->GetBlockTime())); + entry.push_back(Pair("blocktime", pindex->GetBlockTime())); } else entry.push_back(Pair("confirmations", 0)); @@ -304,6 +304,7 @@ Value listunspent(const Array& params, bool fHelp) } entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("confirmations",out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); results.push_back(entry); } @@ -349,7 +350,7 @@ Value createrawtransaction(const Array& params, bool fHelp) Array inputs = params[0].get_array(); Object sendTo = params[1].get_obj(); - CTransaction rawTx; + CMutableTransaction rawTx; BOOST_FOREACH(const Value& input, inputs) { @@ -554,11 +555,11 @@ Value signrawtransaction(const Array& params, bool fHelp) vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - vector<CTransaction> txVariants; + vector<CMutableTransaction> txVariants; while (!ssData.empty()) { try { - CTransaction tx; + CMutableTransaction tx; ssData >> tx; txVariants.push_back(tx); } @@ -572,7 +573,7 @@ Value signrawtransaction(const Array& params, bool fHelp) // mergedTx will end up with all the signatures; it // starts as a clone of the rawtx: - CTransaction mergedTx(txVariants[0]); + CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; // Fetch previous transactions (inputs): @@ -713,7 +714,7 @@ Value signrawtransaction(const Array& params, bool fHelp) SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } @@ -770,7 +771,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) catch (std::exception &e) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } - uint256 hashTx = tx.GetHash(); + const uint256 &hashTx = tx.GetHash(); CCoinsViewCache &view = *pcoinsTip; CCoins existingCoins; @@ -780,7 +781,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) // push to local node and sync with wallets CValidationState state; if (AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) - SyncWithWallets(hashTx, tx, NULL); + SyncWithWallets(tx, NULL); else { if(state.IsInvalid()) throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); @@ -790,7 +791,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } - RelayTransaction(tx, hashTx); + RelayTransaction(tx); return hashTx.GetHex(); } diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index d4ceb7f995..18fa075101 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -25,10 +25,10 @@ #include <boost/shared_ptr.hpp> #include "json/json_spirit_writer_template.h" -using namespace std; using namespace boost; using namespace boost::asio; using namespace json_spirit; +using namespace std; static std::string strRPCUserColonPass; @@ -97,16 +97,6 @@ Value ValueFromAmount(int64_t amount) return (double)amount / (double)COIN; } -std::string HexBits(unsigned int nBits) -{ - union { - int32_t nBits; - char cBits[4]; - } uBits; - uBits.nBits = htonl((int32_t)nBits); - return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); -} - uint256 ParseHashV(const Value& v, string strName) { string strHex; @@ -254,6 +244,7 @@ static const CRPCCommand vRPCCommands[] = { "getblocktemplate", &getblocktemplate, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false }, { "getnetworkhashps", &getnetworkhashps, true, false, false }, + { "prioritisetransaction", &prioritisetransaction, true, false, false }, { "submitblock", &submitblock, false, true, false }, /* Raw transactions */ @@ -291,6 +282,7 @@ static const CRPCCommand vRPCCommands[] = { "getwalletinfo", &getwalletinfo, true, false, true }, { "importprivkey", &importprivkey, false, false, true }, { "importwallet", &importwallet, false, false, true }, + { "importaddress", &importaddress, false, false, true }, { "keypoolrefill", &keypoolrefill, true, false, true }, { "listaccounts", &listaccounts, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, @@ -315,7 +307,6 @@ static const CRPCCommand vRPCCommands[] = /* Wallet-enabled mining */ { "getgenerate", &getgenerate, true, false, false }, { "gethashespersec", &gethashespersec, true, false, false }, - { "getwork", &getwork, true, false, true }, { "setgenerate", &setgenerate, true, true, false }, #endif // ENABLE_WALLET }; @@ -393,16 +384,6 @@ bool ClientAllowed(const boost::asio::ip::address& address) return false; } -class AcceptedConnection -{ -public: - virtual ~AcceptedConnection() {} - - virtual std::iostream& stream() = 0; - virtual std::string peer_address_to_string() const = 0; - virtual void close() = 0; -}; - template <typename Protocol> class AcceptedConnectionImpl : public AcceptedConnection { @@ -501,7 +482,7 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, { // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. if (!fUseSSL) - conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; + conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush; conn->close(); } else { @@ -532,7 +513,7 @@ void StartRPCThreads() if(!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( - strprintf("Invalid -rpcallowip subnet specification: %s", strAllow), + strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; @@ -601,7 +582,7 @@ void StartRPCThreads() std::vector<ip::tcp::endpoint> vEndpoints; bool bBindAny = false; - int defaultPort = GetArg("-rpcport", Params().RPCPort()); + int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs { vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort)); @@ -642,7 +623,6 @@ void StartRPCThreads() LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", bindAddress.to_string(), endpoint.port(), bBindAny); boost::system::error_code v6_only_error; boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); - rpc_acceptors.push_back(acceptor); try { acceptor->open(endpoint.protocol()); @@ -658,6 +638,7 @@ void StartRPCThreads() RPCListen(acceptor, *rpc_ssl_context, fUseSSL); fListening = true; + rpc_acceptors.push_back(acceptor); // If dual IPv6/IPv4 bind succesful, skip binding to IPv4 separately if(bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error) break; @@ -700,11 +681,20 @@ void StopRPCThreads() // First, cancel all timers and acceptors // This is not done automatically by ->stop(), and in some cases the destructor of // asio::io_service can hang if this is skipped. + boost::system::error_code ec; BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) - acceptor->cancel(); + { + acceptor->cancel(ec); + if (ec) + LogPrintf("%s: Warning: %s when cancelling acceptor", __func__, ec.message()); + } rpc_acceptors.clear(); BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers) - timer.second->cancel(); + { + timer.second->cancel(ec); + if (ec) + LogPrintf("%s: Warning: %s when cancelling timer", __func__, ec.message()); + } deadlineTimers.clear(); rpc_io_service->stop(); @@ -763,7 +753,7 @@ void JSONRequest::parse(const Value& valRequest) if (valMethod.type() != str_type) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); - if (strMethod != "getwork" && strMethod != "getblocktemplate") + if (strMethod != "getblocktemplate") LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod); // Parse params @@ -810,6 +800,71 @@ static string JSONRPCExecBatch(const Array& vReq) return write_string(Value(ret), false) + "\n"; } +static bool HTTPReq_JSONRPC(AcceptedConnection *conn, + string& strRequest, + map<string, string>& mapHeaders, + bool fRun) +{ + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; + return false; + } + + if (!HTTPAuthorized(mapHeaders)) + { + LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); + /* Deter brute-forcing short passwords. + If this results in a DoS the user really + shouldn't have their RPC port exposed. */ + if (mapArgs["-rpcpassword"].size() < 20) + MilliSleep(250); + + conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; + return false; + } + + JSONRequest jreq; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + + string strReply; + + // singleton request + if (valRequest.type() == obj_type) { + jreq.parse(valRequest); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + + // Send reply + strReply = JSONRPCReply(result, Value::null, jreq.id); + + // array of requests + } else if (valRequest.type() == array_type) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + + conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; + } + catch (Object& objError) + { + ErrorReply(conn->stream(), objError, jreq.id); + return false; + } + catch (std::exception& e) + { + ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + return false; + } + return true; +} + void ServiceConnection(AcceptedConnection *conn) { bool fRun = true; @@ -826,67 +881,15 @@ void ServiceConnection(AcceptedConnection *conn) // Read HTTP message headers and body ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); - if (strURI != "/") { - conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; - break; - } - - // Check authorization - if (mapHeaders.count("authorization") == 0) - { - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; - } - if (!HTTPAuthorized(mapHeaders)) - { - LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); - /* Deter brute-forcing short passwords. - If this results in a DoS the user really - shouldn't have their RPC port exposed. */ - if (mapArgs["-rpcpassword"].size() < 20) - MilliSleep(250); - - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; - } + // HTTP Keep-Alive is false; close connection immediately if (mapHeaders["connection"] == "close") fRun = false; - JSONRequest jreq; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest)) - throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - - string strReply; - - // singleton request - if (valRequest.type() == obj_type) { - jreq.parse(valRequest); - - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - - // Send reply - strReply = JSONRPCReply(result, Value::null, jreq.id); - - // array of requests - } else if (valRequest.type() == array_type) - strReply = JSONRPCExecBatch(valRequest.get_array()); - else - throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); - - conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; - } - catch (Object& objError) - { - ErrorReply(conn->stream(), objError, jreq.id); - break; - } - catch (std::exception& e) - { - ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + if (strURI == "/") { + if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) + break; + } else { + conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; break; } } diff --git a/src/rpcserver.h b/src/rpcserver.h index 73e8b9426c..e32eb975a1 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -21,6 +21,16 @@ class CBlockIndex; class CNetAddr; +class AcceptedConnection +{ +public: + virtual ~AcceptedConnection() {} + + virtual std::iostream& stream() = 0; + virtual std::string peer_address_to_string() const = 0; + virtual void close() = 0; +}; + /* Start RPC threads */ void StartRPCThreads(); /* Alternative to StartRPCThreads for the GUI, when no server is @@ -106,7 +116,6 @@ extern int64_t nWalletUnlockTime; extern int64_t AmountFromValue(const json_spirit::Value& value); extern json_spirit::Value ValueFromAmount(int64_t amount); extern double GetDifficulty(const CBlockIndex* blockindex = NULL); -extern std::string HexBits(unsigned int nBits); extern std::string HelpRequiringPassphrase(); extern std::string HelpExampleCli(std::string methodname, std::string args); extern std::string HelpExampleRpc(std::string methodname, std::string args); @@ -122,6 +131,7 @@ extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fH extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp); @@ -130,7 +140,7 @@ extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHe extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value prioritisetransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index f376ab6b63..1e46129065 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -8,6 +8,7 @@ #include "init.h" #include "net.h" #include "netbase.h" +#include "timedata.h" #include "util.h" #include "wallet.h" #include "walletdb.h" @@ -49,7 +50,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) { entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); entry.push_back(Pair("blockindex", wtx.nIndex)); - entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); + entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); @@ -57,6 +58,10 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); + Array respends; + BOOST_FOREACH(const uint256& respend, wtx.GetConflicts(false)) + respends.push_back(respend.GetHex()); + entry.push_back(Pair("respendsobserved", respends)); entry.push_back(Pair("time", wtx.GetTxTime())); entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) @@ -341,7 +346,7 @@ Value sendtoaddress(const Array& params, bool fHelp) EnsureWalletIsUnlocked(); - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -552,7 +557,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) } -int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) { int64_t nBalance = 0; @@ -564,7 +569,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi continue; int64_t nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee); + wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) nBalance += nReceived; @@ -577,18 +582,18 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi return nBalance; } -int64_t GetAccountBalance(const string& strAccount, int nMinDepth) +int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) { CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); } Value getbalance(const Array& params, bool fHelp) { - if (fHelp || params.size() > 2) + if (fHelp || params.size() > 3) throw runtime_error( - "getbalance ( \"account\" minconf )\n" + "getbalance ( \"account\" minconf includeWatchonly )\n" "\nIf account is not specified, returns the server's total available balance.\n" "If account is specified, returns the balance in the account.\n" "Note that the account \"\" is not the same as leaving the parameter out.\n" @@ -596,6 +601,7 @@ Value getbalance(const Array& params, bool fHelp) "\nArguments:\n" "1. \"account\" (string, optional) The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n" "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" "\nExamples:\n" @@ -617,6 +623,10 @@ Value getbalance(const Array& params, bool fHelp) int nMinDepth = 1; if (params.size() > 1) nMinDepth = params[1].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; if (params[0].get_str() == "*") { // Calculate total balance a different way from GetBalance() @@ -633,7 +643,7 @@ Value getbalance(const Array& params, bool fHelp) string strSentAccount; list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listSent; - wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); + wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (wtx.GetDepthInMainChain() >= nMinDepth) { BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) @@ -648,7 +658,7 @@ Value getbalance(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); - int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter); return ValueFromAmount(nBalance); } @@ -776,12 +786,12 @@ Value sendfrom(const Array& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send - string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -853,7 +863,7 @@ Value sendmany(const Array& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); if (totalAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -922,10 +932,12 @@ struct tallyitem int64_t nAmount; int nConf; vector<uint256> txids; + bool fIsWatchonly; tallyitem() { nAmount = 0; nConf = std::numeric_limits<int>::max(); + fIsWatchonly = false; } }; @@ -941,6 +953,11 @@ Value ListReceived(const Array& params, bool fByAccounts) if (params.size() > 1) fIncludeEmpty = params[1].get_bool(); + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + // Tally map<CBitcoinAddress, tallyitem> mapTally; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) @@ -957,13 +974,19 @@ Value ListReceived(const Array& params, bool fByAccounts) BOOST_FOREACH(const CTxOut& txout, wtx.vout) { CTxDestination address; - if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address)) + if (!ExtractDestination(txout.scriptPubKey, address)) + continue; + + isminefilter mine = IsMine(*pwalletMain, address); + if(!(mine & filter)) continue; tallyitem& item = mapTally[address]; item.nAmount += txout.nValue; item.nConf = min(item.nConf, nDepth); item.txids.push_back(wtx.GetHash()); + if (mine & ISMINE_WATCH_ONLY) + item.fIsWatchonly = true; } } @@ -980,10 +1003,12 @@ Value ListReceived(const Array& params, bool fByAccounts) int64_t nAmount = 0; int nConf = std::numeric_limits<int>::max(); + bool fIsWatchonly = false; if (it != mapTally.end()) { nAmount = (*it).second.nAmount; nConf = (*it).second.nConf; + fIsWatchonly = (*it).second.fIsWatchonly; } if (fByAccounts) @@ -991,10 +1016,13 @@ Value ListReceived(const Array& params, bool fByAccounts) tallyitem& item = mapAccountTally[strAccount]; item.nAmount += nAmount; item.nConf = min(item.nConf, nConf); + item.fIsWatchonly = fIsWatchonly; } else { Object obj; + if(fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("address", address.ToString())); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); @@ -1019,6 +1047,8 @@ Value ListReceived(const Array& params, bool fByAccounts) int64_t nAmount = (*it).second.nAmount; int nConf = (*it).second.nConf; Object obj; + if((*it).second.fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("account", (*it).first)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); @@ -1031,17 +1061,19 @@ Value ListReceived(const Array& params, bool fByAccounts) Value listreceivedbyaddress(const Array& params, bool fHelp) { - if (fHelp || params.size() > 2) + if (fHelp || params.size() > 3) throw runtime_error( - "listreceivedbyaddress ( minconf includeempty )\n" + "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" "\nList balances by receiving address.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. includeempty (numeric, optional, dafault=false) Whether to include addresses that haven't received any payments.\n" + "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" "\nResult:\n" "[\n" " {\n" + " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n" " \"address\" : \"receivingaddress\", (string) The receiving address\n" " \"account\" : \"accountname\", (string) 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" @@ -1053,7 +1085,7 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) "\nExamples:\n" + HelpExampleCli("listreceivedbyaddress", "") + HelpExampleCli("listreceivedbyaddress", "6 true") - + HelpExampleRpc("listreceivedbyaddress", "6, true") + + HelpExampleRpc("listreceivedbyaddress", "6, true, true") ); return ListReceived(params, false); @@ -1061,17 +1093,19 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) Value listreceivedbyaccount(const Array& params, bool fHelp) { - if (fHelp || params.size() > 2) + if (fHelp || params.size() > 3) throw runtime_error( - "listreceivedbyaccount ( minconf includeempty )\n" + "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" "\nList balances by account.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" + "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" "\nResult:\n" "[\n" " {\n" + " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n" " \"account\" : \"accountname\", (string) The account name of the receiving account\n" " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" @@ -1082,7 +1116,7 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) "\nExamples:\n" + HelpExampleCli("listreceivedbyaccount", "") + HelpExampleCli("listreceivedbyaccount", "6 true") - + HelpExampleRpc("listreceivedbyaccount", "6, true") + + HelpExampleRpc("listreceivedbyaccount", "6, true, true") ); return ListReceived(params, true); @@ -1095,16 +1129,17 @@ static void MaybePushAddress(Object & entry, const CTxDestination &dest) entry.push_back(Pair("address", addr.ToString())); } -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter) { int64_t nFee; string strSentAccount; list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listSent; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); bool fAllAccounts = (strAccount == string("*")); + bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); // Sent if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) @@ -1112,6 +1147,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) { Object entry; + if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", strSentAccount)); MaybePushAddress(entry, s.first); entry.push_back(Pair("category", "send")); @@ -1134,6 +1171,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe if (fAllAccounts || (account == strAccount)) { Object entry; + if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", account)); MaybePushAddress(entry, r.first); if (wtx.IsCoinBase()) @@ -1177,16 +1216,16 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar Value listtransactions(const Array& params, bool fHelp) { - if (fHelp || params.size() > 3) + if (fHelp || params.size() > 4) throw runtime_error( - "listtransactions ( \"account\" count from )\n" + "listtransactions ( \"account\" count from includeWatchonly)\n" "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" "\nArguments:\n" "1. \"account\" (string, optional) The account name. If not included, it will list all transactions for all accounts.\n" " If \"\" is set, it will list transactions for the default account.\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. from (numeric, optional, default=0) The number of transactions to skip\n" - + "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" "\nResult:\n" "[\n" " {\n" @@ -1210,6 +1249,12 @@ Value listtransactions(const Array& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" @@ -1240,6 +1285,10 @@ Value listtransactions(const Array& params, bool fHelp) int nFrom = 0; if (params.size() > 2) nFrom = params[2].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 3) + if(params[3].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; if (nCount < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); @@ -1256,7 +1305,7 @@ Value listtransactions(const Array& params, bool fHelp) { CWalletTx *const pwtx = (*it).second.first; if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret); + ListTransactions(*pwtx, strAccount, 0, true, ret, filter); CAccountingEntry *const pacentry = (*it).second.second; if (pacentry != 0) AcentryToJSON(*pacentry, strAccount, ret); @@ -1284,12 +1333,13 @@ Value listtransactions(const Array& params, bool fHelp) Value listaccounts(const Array& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() > 2) throw runtime_error( - "listaccounts ( minconf )\n" + "listaccounts ( minconf includeWatchonly)\n" "\nReturns Object that has account names as keys, account balances as values.\n" "\nArguments:\n" - "1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n" + "1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n" + "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" "\nResult:\n" "{ (json object where keys are account names, and values are numeric balances\n" " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" @@ -1309,10 +1359,14 @@ Value listaccounts(const Array& params, bool fHelp) int nMinDepth = 1; if (params.size() > 0) nMinDepth = params[0].get_int(); + isminefilter includeWatchonly = ISMINE_SPENDABLE; + if(params.size() > 1) + if(params[1].get_bool()) + includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; map<string, int64_t> mapAccountBalances; BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { - if (IsMine(*pwalletMain, entry.first)) // This address belongs to me + if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me mapAccountBalances[entry.second.name] = 0; } @@ -1326,7 +1380,7 @@ Value listaccounts(const Array& params, bool fHelp) int nDepth = wtx.GetDepthInMainChain(); if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) continue; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) mapAccountBalances[strSentAccount] -= s.second; @@ -1356,11 +1410,12 @@ Value listsinceblock(const Array& params, bool fHelp) { if (fHelp) throw runtime_error( - "listsinceblock ( \"blockhash\" target-confirmations )\n" + "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" "\nArguments:\n" "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" + "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" "\nResult:\n" "{\n" " \"transactions\": [\n" @@ -1375,6 +1430,12 @@ Value listsinceblock(const Array& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" @@ -1390,6 +1451,7 @@ Value listsinceblock(const Array& params, bool fHelp) CBlockIndex *pindex = NULL; int target_confirms = 1; + isminefilter filter = ISMINE_SPENDABLE; if (params.size() > 0) { @@ -1409,6 +1471,10 @@ Value listsinceblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } + if(params.size() > 2) + if(params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; Array transactions; @@ -1418,7 +1484,7 @@ Value listsinceblock(const Array& params, bool fHelp) CWalletTx tx = (*it).second; if (depth == -1 || tx.GetDepthInMainChain() < depth) - ListTransactions(tx, "*", 0, true, transactions); + ListTransactions(tx, "*", 0, true, transactions, filter); } CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; @@ -1433,12 +1499,13 @@ Value listsinceblock(const Array& params, bool fHelp) Value gettransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "gettransaction \"txid\"\n" "\nGet detailed information about in-wallet transaction <txid>\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" + "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" @@ -1447,6 +1514,12 @@ Value gettransaction(const Array& params, bool fHelp) " \"blockindex\" : xx, (numeric) The block index\n" " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"walletconflicts\" : [\n" + " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" + " ],\n" + " \"respendsobserved\" : [\n" + " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" + " ],\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" " \"details\" : [\n" @@ -1469,24 +1542,29 @@ Value gettransaction(const Array& params, bool fHelp) uint256 hash; hash.SetHex(params[0].get_str()); + isminefilter filter = ISMINE_SPENDABLE; + if(params.size() > 1) + if(params[1].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + Object entry; if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - int64_t nCredit = wtx.GetCredit(); - int64_t nDebit = wtx.GetDebit(); + int64_t nCredit = wtx.GetCredit(filter); + int64_t nDebit = wtx.GetDebit(filter); int64_t nNet = nCredit - nDebit; - int64_t nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); - if (wtx.IsFromMe()) + if (wtx.IsFromMe(filter)) entry.push_back(Pair("fee", ValueFromAmount(nFee))); WalletTxToJSON(wtx, entry); Array details; - ListTransactions(wtx, "*", 0, false, details); + ListTransactions(wtx, "*", 0, false, details, filter); entry.push_back(Pair("details", details)); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/script.cpp b/src/script.cpp index 11cdfef950..238a25e72d 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -9,6 +9,9 @@ #include "hash.h" #include "key.h" #include "keystore.h" +#include "crypto/sha1.h" +#include "crypto/sha2.h" +#include "crypto/ripemd160.h" #include "sync.h" #include "uint256.h" #include "util.h" @@ -801,21 +804,15 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) - RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + CRIPEMD160().Write(&vch[0], vch.size()).Finalize(&vchHash[0]); else if (opcode == OP_SHA1) - SHA1(&vch[0], vch.size(), &vchHash[0]); + CSHA1().Write(&vch[0], vch.size()).Finalize(&vchHash[0]); else if (opcode == OP_SHA256) - SHA256(&vch[0], vch.size(), &vchHash[0]); + CSHA256().Write(&vch[0], vch.size()).Finalize(&vchHash[0]); else if (opcode == OP_HASH160) - { - uint160 hash160 = Hash160(vch); - memcpy(&vchHash[0], &hash160, sizeof(hash160)); - } + CHash160().Write(&vch[0], vch.size()).Finalize(&vchHash[0]); else if (opcode == OP_HASH256) - { - uint256 hash = Hash(vch.begin(), vch.end()); - memcpy(&vchHash[0], &hash, sizeof(hash)); - } + CHash256().Write(&vch[0], vch.size()).Finalize(&vchHash[0]); popstack(stack); stack.push_back(vchHash); } @@ -977,6 +974,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co namespace { + /** Wrapper that serializes like CTransaction, but with the modifications * required for the signature hash done in-place */ @@ -1069,7 +1067,8 @@ public: ::Serialize(s, txTo.nLockTime, nType, nVersion); } }; -} + +} // anon namespace uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) { @@ -1095,7 +1094,6 @@ uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } - // Valid signature cache, to avoid doing expensive ECDSA signature checking // twice for every transaction (once when accepted into memory pool, and // again when accepted into the block chain) @@ -1211,7 +1209,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); // Empty, provably prunable, data-carrying output - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + if (GetBoolArg("-datacarrier", true)) + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); } @@ -1457,36 +1456,49 @@ public: bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } }; -bool IsMine(const CKeyStore &keystore, const CTxDestination &dest) +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) { - return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); + CScript script; + script.SetDestination(dest); + return IsMine(keystore, script); } -bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { vector<valtype> vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + return ISMINE_NO; + } CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: case TX_NULL_DATA: - return false; + break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return keystore.HaveKey(keyID); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - return keystore.HaveKey(keyID); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; case TX_SCRIPTHASH: { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; - if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) - return false; - return IsMine(keystore, subscript); + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == ISMINE_SPENDABLE) + return ret; + } + break; } case TX_MULTISIG: { @@ -1496,10 +1508,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - return HaveKeys(keys, keystore) == keys.size(); + if (HaveKeys(keys, keystore) == keys.size()) + return ISMINE_SPENDABLE; + break; } } - return false; + + if (keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + return ISMINE_NO; } bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) @@ -1639,7 +1656,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1674,7 +1691,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0); } -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1692,7 +1709,7 @@ static CScript PushAll(const vector<valtype>& values) return result; } -static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn, const vector<valtype>& vSolutions, vector<valtype>& sigs1, vector<valtype>& sigs2) { diff --git a/src/script.h b/src/script.h index a282e7dc04..e36be2db9e 100644 --- a/src/script.h +++ b/src/script.h @@ -20,6 +20,7 @@ class CCoins; class CKeyStore; class CTransaction; +struct CMutableTransaction; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes @@ -193,6 +194,17 @@ enum SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length }; +/** IsMine() return codes */ +enum isminetype +{ + ISMINE_NO = 0, + ISMINE_WATCH_ONLY = 1, + ISMINE_SPENDABLE = 2, + ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE +}; +/** used for bitflags of isminetype */ +typedef uint8_t isminefilter; + // Mandatory script verification flags that all new blocks must comply with for // them to be valid. (but old blocks may not comply with) Currently just P2SH, // but in the future other flags may be added, such as a soft-fork to enforce @@ -769,12 +781,12 @@ public: void Serialize(Stream &s, int nType, int nVersion) const { std::vector<unsigned char> compr; if (Compress(compr)) { - s << CFlatData(&compr[0], &compr[compr.size()]); + s << CFlatData(compr); return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); - s << CFlatData(&script[0], &script[script.size()]); + s << CFlatData(script); } template<typename Stream> @@ -783,13 +795,13 @@ public: s >> VARINT(nSize); if (nSize < nSpecialScripts) { std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); - s >> REF(CFlatData(&vch[0], &vch[vch.size()])); + s >> REF(CFlatData(vch)); Decompress(nSize, vch); return; } nSize -= nSpecialScripts; script.resize(nSize); - s >> REF(CFlatData(&script[0], &script[script.size()])); + s >> REF(CFlatData(script)); } }; @@ -800,13 +812,13 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); -bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, diff --git a/src/secp256k1/.empty b/src/secp256k1/.empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/secp256k1/.empty diff --git a/src/serialize.h b/src/serialize.h index 1341746592..5ac85554c6 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -37,6 +37,34 @@ inline T& REF(const T& val) return const_cast<T&>(val); } +/** Get begin pointer of vector (non-const version). + * @note These functions avoid the undefined case of indexing into an empty + * vector, as well as that of indexing after the end of the vector. + */ +template <class T, class TAl> +inline T* begin_ptr(std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get begin pointer of vector (const version) */ +template <class T, class TAl> +inline const T* begin_ptr(const std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get end pointer of vector (non-const version) */ +template <class T, class TAl> +inline T* end_ptr(std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} +/** Get end pointer of vector (const version) */ +template <class T, class TAl> +inline const T* end_ptr(const std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} + ///////////////////////////////////////////////////////////////// // // Templates for serializing to anything that looks like a stream, @@ -318,6 +346,12 @@ protected: char* pend; public: CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + template <class T, class TAl> + explicit CFlatData(std::vector<T,TAl> &v) + { + pbegin = (char*)begin_ptr(v); + pend = (char*)end_ptr(v); + } char* begin() { return pbegin; } const char* begin() const { return pbegin; } char* end() { return pend; } diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index fb06fb3435..5e17555e7a 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -11,6 +11,7 @@ #include "keystore.h" #include "main.h" #include "net.h" +#include "pow.h" #include "script.h" #include "serialize.h" @@ -167,7 +168,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) // 50 orphan transactions: for (int i = 0; i < 50; i++) { - CTransaction tx; + CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; tx.vin[0].prevout.hash = GetRandHash(); @@ -184,7 +185,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) { CTransaction txPrev = RandomOrphan(); - CTransaction tx; + CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; tx.vin[0].prevout.hash = txPrev.GetHash(); @@ -201,7 +202,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) { CTransaction txPrev = RandomOrphan(); - CTransaction tx; + CMutableTransaction tx; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); @@ -230,91 +231,4 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) BOOST_CHECK(mapOrphanTransactionsByPrev.empty()); } -BOOST_AUTO_TEST_CASE(DoS_checkSig) -{ - // Test signature caching code (see key.cpp Verify() methods) - - CKey key; - key.MakeNewKey(true); - CBasicKeyStore keystore; - keystore.AddKey(key); - unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; - - // 100 orphan transactions: - static const int NPREV=100; - CTransaction orphans[NPREV]; - for (int i = 0; i < NPREV; i++) - { - CTransaction& tx = orphans[i]; - tx.vin.resize(1); - tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = GetRandHash(); - tx.vin[0].scriptSig << OP_1; - tx.vout.resize(1); - tx.vout[0].nValue = 1*CENT; - tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - - AddOrphanTx(tx); - } - - // Create a transaction that depends on orphans: - CTransaction tx; - tx.vout.resize(1); - tx.vout[0].nValue = 1*CENT; - tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - tx.vin.resize(NPREV); - for (unsigned int j = 0; j < tx.vin.size(); j++) - { - tx.vin[j].prevout.n = 0; - tx.vin[j].prevout.hash = orphans[j].GetHash(); - } - // Creating signatures primes the cache: - boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time(); - for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j)); - boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time(); - boost::posix_time::time_duration msdiff = mst2 - mst1; - long nOneValidate = msdiff.total_milliseconds(); - if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate); - - // ... now validating repeatedly should be quick: - // 2.8GHz machine, -g build: Sign takes ~760ms, - // uncached Verify takes ~250ms, cached Verify takes ~50ms - // (for 100 single-signature inputs) - mst1 = boost::posix_time::microsec_clock::local_time(); - for (unsigned int i = 0; i < 5; i++) - for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); - mst2 = boost::posix_time::microsec_clock::local_time(); - msdiff = mst2 - mst1; - long nManyValidate = msdiff.total_milliseconds(); - if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate); - - BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed"); - - // Empty a signature, validation should fail: - CScript save = tx.vin[0].scriptSig; - tx.vin[0].scriptSig = CScript(); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); - tx.vin[0].scriptSig = save; - - // Swap signatures, validation should fail: - std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); - BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); - BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL)); - std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); - - // Exercise -maxsigcachesize code: - mapArgs["-maxsigcachesize"] = "10"; - // Generate a new, different signature for vin[0] to trigger cache clear: - CScript oldSig = tx.vin[0].scriptSig; - BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); - BOOST_CHECK(tx.vin[0].scriptSig != oldSig); - for (unsigned int j = 0; j < tx.vin.size(); j++) - BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); - mapArgs.erase("-maxsigcachesize"); - - LimitOrphanTxSize(0); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index e2a75da349..4bee0f6b6e 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -83,13 +83,21 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; - --wtx.nLockTime; // Just to change the hash :) + { + CMutableTransaction tx(wtx); + --tx.nLockTime; // Just to change the hash :) + *static_cast<CTransaction*>(&wtx) = CTransaction(tx); + } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; wtx.mapValue["comment"] = "x"; - --wtx.nLockTime; // Just to change the hash :) + { + CMutableTransaction tx(wtx); + --tx.nLockTime; // Just to change the hash :) + *static_cast<CTransaction*>(&wtx) = CTransaction(tx); + } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index b81a19cfd8..0ac3e9a363 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -142,9 +142,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); if (isTestnet) - SelectParams(CChainParams::TESTNET); + SelectParams(CBaseChainParams::TESTNET); else - SelectParams(CChainParams::MAIN); + SelectParams(CBaseChainParams::MAIN); if(isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); } } - SelectParams(CChainParams::MAIN); + SelectParams(CBaseChainParams::MAIN); } // Goal: check that generated keys match test vectors @@ -198,9 +198,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); if (isTestnet) - SelectParams(CChainParams::TESTNET); + SelectParams(CBaseChainParams::TESTNET); else - SelectParams(CChainParams::MAIN); + SelectParams(CBaseChainParams::MAIN); if(isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CTxDestination nodest = CNoDestination(); BOOST_CHECK(!dummyAddr.Set(nodest)); - SelectParams(CChainParams::MAIN); + SelectParams(CBaseChainParams::MAIN); } // Goal: check that base58 parsing code is robust against a variety of corrupted data diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index abedd3093c..2cdafa4bdd 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -45,12 +45,16 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) expected[i] = (char)vch[i]; BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); + + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); + filter.clear(); + BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!"); } BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) { // Same test as bloom_create_insert_serialize, but we add a nTweak of 100 - CBloomFilter filter(3, 0.01, 2147483649, BLOOM_UPDATE_ALL); + CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL); filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); @@ -118,33 +122,33 @@ BOOST_AUTO_TEST_CASE(bloom_match) CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(uint256("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match tx hash"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match tx hash"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); // byte-reversed tx hash filter.insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized tx hash"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized tx hash"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input signature"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input signature"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input pub key"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input pub key"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address"); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx, spendingTx.GetHash()), "Simple Bloom filter didn't add output"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx), "Simple Bloom filter didn't add output"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match COutPoint"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); COutPoint prevOutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0); @@ -154,23 +158,23 @@ BOOST_AUTO_TEST_CASE(bloom_match) memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int)); filter.insert(data); } - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized COutPoint"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(uint256("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436")); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random tx hash"); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random tx hash"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431")); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random address"); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1)); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(COutPoint(uint256("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); } BOOST_AUTO_TEST_CASE(merkle_block_1) diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp new file mode 100644 index 0000000000..7bd98fa381 --- /dev/null +++ b/src/test/crypto_tests.cpp @@ -0,0 +1,203 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "crypto/ripemd160.h" +#include "crypto/sha1.h" +#include "crypto/sha2.h" +#include "util.h" + +#include <vector> + +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(crypto_tests) + +template<typename Hasher, typename In, typename Out> +void TestVector(const Hasher &h, const In &in, const Out &out) { + Out hash; + BOOST_CHECK(out.size() == h.OUTPUT_SIZE); + hash.resize(out.size()); + { + // Test that writing the whole input string at once works. + Hasher(h).Write((unsigned char*)&in[0], in.size()).Finalize(&hash[0]); + BOOST_CHECK(hash == out); + } + for (int i=0; i<32; i++) { + // Test that writing the string broken up in random pieces works. + Hasher hasher(h); + size_t pos = 0; + while (pos < in.size()) { + size_t len = insecure_rand() % ((in.size() - pos + 1) / 2 + 1); + hasher.Write((unsigned char*)&in[pos], len); + pos += len; + if (pos > 0 && pos + 2 * out.size() > in.size()) { + // Test that writing the rest at once to a copy of a hasher works. + Hasher(hasher).Write((unsigned char*)&in[pos], in.size() - pos).Finalize(&hash[0]); + BOOST_CHECK(hash == out); + } + } + hasher.Finalize(&hash[0]); + BOOST_CHECK(hash == out); + } +} + +void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout));} +void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout));} +void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout));} +void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout));} + +void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { + std::vector<unsigned char> key = ParseHex(hexkey); + TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); +} + +std::string LongTestString(void) { + std::string ret; + for (int i=0; i<200000; i++) { + ret += (unsigned char)(i); + ret += (unsigned char)(i >> 4); + ret += (unsigned char)(i >> 8); + ret += (unsigned char)(i >> 12); + ret += (unsigned char)(i >> 16); + } + return ret; +} + +const std::string test1 = LongTestString(); + +BOOST_AUTO_TEST_CASE(ripemd160_testvectors) { + TestRIPEMD160("", "9c1185a5c5e9fc54612808977ee8f548b2258d31"); + TestRIPEMD160("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); + TestRIPEMD160("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"); + TestRIPEMD160("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9"); + TestRIPEMD160("RIPEMD160 is considered to be safe", "a7d78608c7af8a8e728778e81576870734122b66"); + TestRIPEMD160("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "12a053384a9c0c88e405a06c27dcf49ada62eb2b"); + TestRIPEMD160("For this sample, this 63-byte string will be used as input data", + "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11"); + TestRIPEMD160("This is exactly 64 bytes long, not counting the terminating byte", + "eda31d51d3a623b81e19eb02e24ff65d27d67b37"); + TestRIPEMD160(std::string(1000000, 'a'), "52783243c1697bdbe16d37f97f68f08325dc1528"); + TestRIPEMD160(test1, "464243587bd146ea835cdf57bdae582f25ec45f1"); +} + +BOOST_AUTO_TEST_CASE(sha1_testvectors) { + TestSHA1("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); + TestSHA1("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"); + TestSHA1("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"); + TestSHA1("secure hash algorithm", "d4d6d2f0ebe317513bbd8d967d89bac5819c2f60"); + TestSHA1("SHA1 is considered to be safe", "f2b6650569ad3a8720348dd6ea6c497dee3a842a"); + TestSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "84983e441c3bd26ebaae4aa1f95129e5e54670f1"); + TestSHA1("For this sample, this 63-byte string will be used as input data", + "4f0ea5cd0585a23d028abdc1a6684e5a8094dc49"); + TestSHA1("This is exactly 64 bytes long, not counting the terminating byte", + "fb679f23e7d1ce053313e66e127ab1b444397057"); + TestSHA1(std::string(1000000, 'a'), "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + TestSHA1(test1, "b7755760681cbfd971451668f32af5774f4656b5"); +} + +BOOST_AUTO_TEST_CASE(sha256_testvectors) { + TestSHA256("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + TestSHA256("abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + TestSHA256("message digest", + "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); + TestSHA256("secure hash algorithm", + "f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"); + TestSHA256("SHA256 is considered to be safe", + "6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"); + TestSHA256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + TestSHA256("For this sample, this 63-byte string will be used as input data", + "f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"); + TestSHA256("This is exactly 64 bytes long, not counting the terminating byte", + "ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"); + TestSHA256("As Bitcoin relies on 80 byte header hashes, we want to have an example for that.", + "7406e8de7d6e4fffc573daef05aefb8806e7790f55eab5576f31349743cca743"); + TestSHA256(std::string(1000000, 'a'), + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + TestSHA256(test1, "a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"); +} + +BOOST_AUTO_TEST_CASE(sha512_testvectors) { + TestSHA512("", + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" + "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + TestSHA512("abc", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); + TestSHA512("message digest", + "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f33" + "09e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"); + TestSHA512("secure hash algorithm", + "7746d91f3de30c68cec0dd693120a7e8b04d8073cb699bdce1a3f64127bca7a3" + "d5db502e814bb63c063a7a5043b2df87c61133395f4ad1edca7fcf4b30c3236e"); + TestSHA512("SHA512 is considered to be safe", + "099e6468d889e1c79092a89ae925a9499b5408e01b66cb5b0a3bd0dfa51a9964" + "6b4a3901caab1318189f74cd8cf2e941829012f2449df52067d3dd5b978456c2"); + TestSHA512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" + "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); + TestSHA512("For this sample, this 63-byte string will be used as input data", + "b3de4afbc516d2478fe9b518d063bda6c8dd65fc38402dd81d1eb7364e72fb6e" + "6663cf6d2771c8f5a6da09601712fb3d2a36c6ffea3e28b0818b05b0a8660766"); + TestSHA512("This is exactly 64 bytes long, not counting the terminating byte", + "70aefeaa0e7ac4f8fe17532d7185a289bee3b428d950c14fa8b713ca09814a38" + "7d245870e007a80ad97c369d193e41701aa07f3221d15f0e65a1ff970cedf030"); + TestSHA512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" + "ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" + "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); + TestSHA512(std::string(1000000, 'a'), + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" + "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + TestSHA512(test1, + "40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de594" + "37de8c3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"); +} + +BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) { + // test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 + TestHMACSHA512("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "4869205468657265", + "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"); + TestHMACSHA512("4a656665", + "7768617420646f2079612077616e7420666f72206e6f7468696e673f", + "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"); + TestHMACSHA512("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddddddd", + "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"); + TestHMACSHA512("0102030405060708090a0b0c0d0e0f10111213141516171819", + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"); + TestHMACSHA512("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa", + "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" + "65204b6579202d2048617368204b6579204669727374", + "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"); + TestHMACSHA512("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa", + "5468697320697320612074657374207573696e672061206c6172676572207468" + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" + "647320746f20626520686173686564206265666f7265206265696e6720757365" + "642062792074686520484d414320616c676f726974686d2e", + "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/hmac_tests.cpp b/src/test/hmac_tests.cpp deleted file mode 100644 index 780ce480ce..0000000000 --- a/src/test/hmac_tests.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2013 The Bitcoin Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "hash.h" -#include "util.h" - -#include <boost/test/unit_test.hpp> - -using namespace std; - -BOOST_AUTO_TEST_SUITE(hmac_tests) - -typedef struct { - const char *pszKey; - const char *pszData; - const char *pszMAC; -} testvec_t; - -// test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 -static const testvec_t vtest[] = { - { - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" - "0b0b0b0b", - "4869205468657265", - "87aa7cdea5ef619d4ff0b4241a1d6cb0" - "2379f4e2ce4ec2787ad0b30545e17cde" - "daa833b7d6b8a702038b274eaea3f4e4" - "be9d914eeb61f1702e696c203a126854" - }, - { - "4a656665", - "7768617420646f2079612077616e7420" - "666f72206e6f7468696e673f", - "164b7a7bfcf819e2e395fbe73b56e0a3" - "87bd64222e831fd610270cd7ea250554" - "9758bf75c05a994a6d034f65f8f0e6fd" - "caeab1a34d4a6b4b636e070a38bce737" - }, - { - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaa", - "dddddddddddddddddddddddddddddddd" - "dddddddddddddddddddddddddddddddd" - "dddddddddddddddddddddddddddddddd" - "dddd", - "fa73b0089d56a284efb0f0756c890be9" - "b1b5dbdd8ee81a3655f83e33b2279d39" - "bf3e848279a722c806b485a47e67c807" - "b946a337bee8942674278859e13292fb" - }, - { - "0102030405060708090a0b0c0d0e0f10" - "111213141516171819", - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - "cdcd", - "b0ba465637458c6990e5a8c5f61d4af7" - "e576d97ff94b872de76f8050361ee3db" - "a91ca5c11aa25eb4d679275cc5788063" - "a5f19741120c4f2de2adebeb10a298dd" - }, - { - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaa", - "54657374205573696e67204c61726765" - "72205468616e20426c6f636b2d53697a" - "65204b6579202d2048617368204b6579" - "204669727374", - "80b24263c7c1a3ebb71493c1dd7be8b4" - "9b46d1f41b4aeec1121b013783f8f352" - "6b56d037e05f2598bd0fd2215d6a1e52" - "95e64f73f63f0aec8b915a985d786598" - }, - { - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaa", - "54686973206973206120746573742075" - "73696e672061206c6172676572207468" - "616e20626c6f636b2d73697a65206b65" - "7920616e642061206c61726765722074" - "68616e20626c6f636b2d73697a652064" - "6174612e20546865206b6579206e6565" - "647320746f2062652068617368656420" - "6265666f7265206265696e6720757365" - "642062792074686520484d414320616c" - "676f726974686d2e", - "e37b6a775dc87dbaa4dfa9f96e5e3ffd" - "debd71f8867289865df5a32d20cdc944" - "b6022cac3c4982b10d5eeb55c3e4de15" - "134676fb6de0446065c97440fa8c6a58" - } -}; - -BOOST_AUTO_TEST_CASE(hmacsha512_testvectors) -{ - for (unsigned int n=0; n<sizeof(vtest)/sizeof(vtest[0]); n++) - { - vector<unsigned char> vchKey = ParseHex(vtest[n].pszKey); - vector<unsigned char> vchData = ParseHex(vtest[n].pszData); - vector<unsigned char> vchMAC = ParseHex(vtest[n].pszMAC); - unsigned char vchTemp[64]; - - HMAC_SHA512_CTX ctx; - HMAC_SHA512_Init(&ctx, &vchKey[0], vchKey.size()); - HMAC_SHA512_Update(&ctx, &vchData[0], vchData.size()); - HMAC_SHA512_Final(&vchTemp[0], &ctx); - - BOOST_CHECK(memcmp(&vchTemp[0], &vchMAC[0], 64) == 0); - - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index b72ba0293f..47977cf295 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -9,8 +9,6 @@ #include <boost/test/unit_test.hpp> -extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); - BOOST_AUTO_TEST_SUITE(miner_tests) static @@ -53,7 +51,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; - CTransaction tx,tx2; + CMutableTransaction tx,tx2; CScript script; uint256 hash; @@ -70,10 +68,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; - pblock->vtx[0].vin[0].scriptSig = CScript(); - pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); - pblock->vtx[0].vin[0].scriptSig.push_back(chainActive.Height()); - pblock->vtx[0].vout[0].scriptPubKey = CScript(); + CMutableTransaction txCoinbase(pblock->vtx[0]); + txCoinbase.vin[0].scriptSig = CScript(); + txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce); + txCoinbase.vin[0].scriptSig.push_back(chainActive.Height()); + txCoinbase.vout[0].scriptPubKey = CScript(); + pblock->vtx[0] = CTransaction(txCoinbase); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); @@ -259,30 +259,4 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } -BOOST_AUTO_TEST_CASE(sha256transform_equality) -{ - unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - - - // unsigned char pstate[32]; - unsigned char pinput[64]; - - int i; - - for (i = 0; i < 32; i++) { - pinput[i] = i; - pinput[i+32] = 0; - } - - uint256 hash; - - SHA256Transform(&hash, pinput, pSHA256InitState); - - BOOST_TEST_MESSAGE(hash.GetHex()); - - uint256 hash_reference("0x2df5e1c65ef9f8cde240d23cae2ec036d31a15ec64bc68f64be242b1da6631f3"); - - BOOST_CHECK(hash == hash_reference); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 3775abd633..452cf084a7 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -55,13 +55,13 @@ BOOST_AUTO_TEST_CASE(multisig_verify) CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom; // Funding transaction + CMutableTransaction txFrom; // Funding transaction txFrom.vout.resize(3); txFrom.vout[0].scriptPubKey = a_and_b; txFrom.vout[1].scriptPubKey = a_or_b; txFrom.vout[2].scriptPubKey = escrow; - CTransaction txTo[3]; // Spending transaction + CMutableTransaction txTo[3]; // Spending transaction for (int i = 0; i < 3; i++) { txTo[i].vin.resize(1); @@ -270,13 +270,13 @@ BOOST_AUTO_TEST_CASE(multisig_Sign) CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom; // Funding transaction + CMutableTransaction txFrom; // Funding transaction txFrom.vout.resize(3); txFrom.vout[0].scriptPubKey = a_and_b; txFrom.vout[1].scriptPubKey = a_or_b; txFrom.vout[2].scriptPubKey = escrow; - CTransaction txTo[3]; // Spending transaction + CMutableTransaction txTo[3]; // Spending transaction for (int i = 0; i < 3; i++) { txTo[i].vin.resize(1); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 7d7e6681df..9dce4daac6 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -36,9 +36,9 @@ BOOST_AUTO_TEST_CASE(pmt_test1) // build a block with some dummy transactions CBlock block; for (unsigned int j=0; j<nTx; j++) { - CTransaction tx; + CMutableTransaction tx; tx.nLockTime = rand(); // actual transaction data doesn't matter; just make the nLockTime's unique - block.vtx.push_back(tx); + block.vtx.push_back(CTransaction(tx)); } // calculate actual merkle root and height diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 9b1290e0ea..a1dc17ba36 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -30,11 +30,11 @@ static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) { // Create dummy to/from transactions: - CTransaction txFrom; + CMutableTransaction txFrom; txFrom.vout.resize(1); txFrom.vout[0].scriptPubKey = scriptPubKey; - CTransaction txTo; + CMutableTransaction txTo; txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(sign) evalScripts[i].SetDestination(standardScripts[i].GetID()); } - CTransaction txFrom; // Funding transaction: + CMutableTransaction txFrom; // Funding transaction: string reason; txFrom.vout.resize(8); for (int i = 0; i < 4; i++) @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(sign) } BOOST_CHECK(IsStandardTx(txFrom, reason)); - CTransaction txTo[8]; // Spending transactions + CMutableTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) { txTo[i].vin.resize(1); @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(set) keystore.AddCScript(inner[i]); } - CTransaction txFrom; // Funding transaction: + CMutableTransaction txFrom; // Funding transaction: string reason; txFrom.vout.resize(4); for (int i = 0; i < 4; i++) @@ -183,7 +183,7 @@ BOOST_AUTO_TEST_CASE(set) } BOOST_CHECK(IsStandardTx(txFrom, reason)); - CTransaction txTo[4]; // Spending transactions + CMutableTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) { txTo[i].vin.resize(1); @@ -256,66 +256,86 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) CCoinsView coinsDummy; CCoinsViewCache coins(coinsDummy); CBasicKeyStore keystore; - CKey key[3]; + CKey key[6]; vector<CPubKey> keys; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 6; i++) { key[i].MakeNewKey(true); keystore.AddKey(key[i]); - keys.push_back(key[i].GetPubKey()); } + for (int i = 0; i < 3; i++) + keys.push_back(key[i].GetPubKey()); - CTransaction txFrom; - txFrom.vout.resize(6); + CMutableTransaction txFrom; + txFrom.vout.resize(7); // First three are standard: CScript pay1; pay1.SetDestination(key[0].GetPubKey().GetID()); keystore.AddCScript(pay1); - CScript payScriptHash1; payScriptHash1.SetDestination(pay1.GetID()); CScript pay1of3; pay1of3.SetMultisig(1, keys); - txFrom.vout[0].scriptPubKey = payScriptHash1; + txFrom.vout[0].scriptPubKey.SetDestination(pay1.GetID()); // P2SH (OP_CHECKSIG) txFrom.vout[0].nValue = 1000; - txFrom.vout[1].scriptPubKey = pay1; + txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG txFrom.vout[1].nValue = 2000; - txFrom.vout[2].scriptPubKey = pay1of3; + txFrom.vout[2].scriptPubKey = pay1of3; // ordinary OP_CHECKMULTISIG txFrom.vout[2].nValue = 3000; - // Last three non-standard: - CScript empty; - keystore.AddCScript(empty); - txFrom.vout[3].scriptPubKey = empty; + // vout[3] is complicated 1-of-3 AND 2-of-3 + // ... that is OK if wrapped in P2SH: + CScript oneAndTwo; + oneAndTwo << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey(); + oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY; + oneAndTwo << OP_2 << key[3].GetPubKey() << key[4].GetPubKey() << key[5].GetPubKey(); + oneAndTwo << OP_3 << OP_CHECKMULTISIG; + keystore.AddCScript(oneAndTwo); + txFrom.vout[3].scriptPubKey.SetDestination(oneAndTwo.GetID()); txFrom.vout[3].nValue = 4000; - // Can't use SetPayToScriptHash, it checks for the empty Script. So: - txFrom.vout[4].scriptPubKey << OP_HASH160 << Hash160(empty) << OP_EQUAL; + + // vout[4] is max sigops: + CScript fifteenSigops; fifteenSigops << OP_1; + for (unsigned i = 0; i < MAX_P2SH_SIGOPS; i++) + fifteenSigops << key[i%3].GetPubKey(); + fifteenSigops << OP_15 << OP_CHECKMULTISIG; + keystore.AddCScript(fifteenSigops); + txFrom.vout[4].scriptPubKey.SetDestination(fifteenSigops.GetID()); txFrom.vout[4].nValue = 5000; - CScript oneOfEleven; - oneOfEleven << OP_1; - for (int i = 0; i < 11; i++) - oneOfEleven << key[0].GetPubKey(); - oneOfEleven << OP_11 << OP_CHECKMULTISIG; - txFrom.vout[5].scriptPubKey.SetDestination(oneOfEleven.GetID()); - txFrom.vout[5].nValue = 6000; + + // vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS + CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG; + keystore.AddCScript(sixteenSigops); + txFrom.vout[5].scriptPubKey.SetDestination(fifteenSigops.GetID()); + txFrom.vout[5].nValue = 5000; + CScript twentySigops; twentySigops << OP_CHECKMULTISIG; + keystore.AddCScript(twentySigops); + txFrom.vout[6].scriptPubKey.SetDestination(twentySigops.GetID()); + txFrom.vout[6].nValue = 6000; + coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); - CTransaction txTo; + CMutableTransaction txTo; txTo.vout.resize(1); txTo.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); - txTo.vin.resize(3); - txTo.vin[0].prevout.n = 0; - txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin.resize(5); + for (int i = 0; i < 5; i++) + { + txTo.vin[i].prevout.n = i; + txTo.vin[i].prevout.hash = txFrom.GetHash(); + } BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); - txTo.vin[1].prevout.n = 1; - txTo.vin[1].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); - txTo.vin[2].prevout.n = 2; - txTo.vin[2].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); + // SignSignature doesn't know how to sign these. We're + // not testing validating signatures, so just create + // dummy signatures that DO include the correct P2SH scripts: + txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast<vector<unsigned char> >(oneAndTwo); + txTo.vin[4].scriptSig << static_cast<vector<unsigned char> >(fifteenSigops); BOOST_CHECK(::AreInputsStandard(txTo, coins)); - BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 1U); + // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) @@ -326,23 +346,29 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[i].scriptSig = t; } - CTransaction txToNonStd; - txToNonStd.vout.resize(1); - txToNonStd.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); - txToNonStd.vout[0].nValue = 1000; - txToNonStd.vin.resize(2); - txToNonStd.vin[0].prevout.n = 4; - txToNonStd.vin[0].prevout.hash = txFrom.GetHash(); - txToNonStd.vin[0].scriptSig << Serialize(empty); - txToNonStd.vin[1].prevout.n = 5; - txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); - txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); - - BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); - BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd, coins), 11U); - - txToNonStd.vin[0].scriptSig.clear(); - BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); + CMutableTransaction txToNonStd1; + txToNonStd1.vout.resize(1); + txToNonStd1.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); + txToNonStd1.vout[0].nValue = 1000; + txToNonStd1.vin.resize(1); + txToNonStd1.vin[0].prevout.n = 5; + txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); + txToNonStd1.vin[0].scriptSig << static_cast<vector<unsigned char> >(sixteenSigops); + + BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U); + + CMutableTransaction txToNonStd2; + txToNonStd2.vout.resize(1); + txToNonStd2.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); + txToNonStd2.vout[0].nValue = 1000; + txToNonStd2.vin.resize(1); + txToNonStd2.vin[0].prevout.n = 6; + txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); + txToNonStd2.vin[0].scriptSig << static_cast<vector<unsigned char> >(twentySigops); + + BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 29d518d707..cba582e941 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -240,11 +240,11 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CScript scriptPubKey12; scriptPubKey12 << OP_1 << key1.GetPubKey() << key2.GetPubKey() << OP_2 << OP_CHECKMULTISIG; - CTransaction txFrom12; + CMutableTransaction txFrom12; txFrom12.vout.resize(1); txFrom12.vout[0].scriptPubKey = scriptPubKey12; - CTransaction txTo12; + CMutableTransaction txTo12; txTo12.vin.resize(1); txTo12.vout.resize(1); txTo12.vin[0].prevout.n = 0; @@ -274,11 +274,11 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) CScript scriptPubKey23; scriptPubKey23 << OP_2 << key1.GetPubKey() << key2.GetPubKey() << key3.GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom23; + CMutableTransaction txFrom23; txFrom23.vout.resize(1); txFrom23.vout[0].scriptPubKey = scriptPubKey23; - CTransaction txTo23; + CMutableTransaction txTo23; txTo23.vin.resize(1); txTo23.vout.resize(1); txTo23.vin[0].prevout.n = 0; @@ -345,11 +345,11 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) keystore.AddKey(key); } - CTransaction txFrom; + CMutableTransaction txFrom; txFrom.vout.resize(1); txFrom.vout[0].scriptPubKey.SetDestination(keys[0].GetPubKey().GetID()); CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; - CTransaction txTo; + CMutableTransaction txTo; txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 353fd60f7b..423ae4a789 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -28,7 +28,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); return 1; } - CTransaction txTmp(txTo); + CMutableTransaction txTmp(txTo); // In case concatenating two scripts ends up with two codeseparators, // or an extra one at the end, this prevents all those possible incompatibilities. @@ -90,7 +90,7 @@ void static RandomScript(CScript &script) { script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; } -void static RandomTransaction(CTransaction &tx, bool fSingle) { +void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { tx.nVersion = insecure_rand(); tx.vin.clear(); tx.vout.clear(); @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(sighash_test) #endif for (int i=0; i<nRandomTests; i++) { int nHashType = insecure_rand(); - CTransaction txTo; + CMutableTransaction txTo; RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); CScript scriptCode; RandomScript(scriptCode); diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp new file mode 100644 index 0000000000..11762c6ea0 --- /dev/null +++ b/src/test/skiplist_tests.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <boost/test/unit_test.hpp> +#include <vector> +#include "main.h" +#include "util.h" + + +#define SKIPLIST_LENGTH 300000 + +BOOST_AUTO_TEST_SUITE(skiplist_tests) + +BOOST_AUTO_TEST_CASE(skiplist_test) +{ + std::vector<CBlockIndex> vIndex(SKIPLIST_LENGTH); + + for (int i=0; i<SKIPLIST_LENGTH; i++) { + vIndex[i].nHeight = i; + vIndex[i].pprev = (i == 0) ? NULL : &vIndex[i - 1]; + vIndex[i].BuildSkip(); + } + + for (int i=0; i<SKIPLIST_LENGTH; i++) { + if (i > 0) { + BOOST_CHECK(vIndex[i].pskip == &vIndex[vIndex[i].pskip->nHeight]); + BOOST_CHECK(vIndex[i].pskip->nHeight < i); + } else { + BOOST_CHECK(vIndex[i].pskip == NULL); + } + } + + for (int i=0; i < 1000; i++) { + int from = insecure_rand() % (SKIPLIST_LENGTH - 1); + int to = insecure_rand() % (from + 1); + + BOOST_CHECK(vIndex[SKIPLIST_LENGTH - 1].GetAncestor(from) == &vIndex[from]); + BOOST_CHECK(vIndex[from].GetAncestor(to) == &vIndex[to]); + BOOST_CHECK(vIndex[from].GetAncestor(0) == &vIndex[0]); + } +} + +BOOST_AUTO_TEST_CASE(getlocator_test) +{ + // Build a main chain 100000 blocks long. + std::vector<uint256> vHashMain(100000); + std::vector<CBlockIndex> vBlocksMain(100000); + for (unsigned int i=0; i<vBlocksMain.size(); i++) { + vHashMain[i] = i; // Set the hash equal to the height, so we can quickly check the distances. + vBlocksMain[i].nHeight = i; + vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL; + vBlocksMain[i].phashBlock = &vHashMain[i]; + vBlocksMain[i].BuildSkip(); + BOOST_CHECK_EQUAL((int)vBlocksMain[i].GetBlockHash().GetLow64(), vBlocksMain[i].nHeight); + BOOST_CHECK(vBlocksMain[i].pprev == NULL || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1); + } + + // Build a branch that splits off at block 49999, 50000 blocks long. + std::vector<uint256> vHashSide(50000); + std::vector<CBlockIndex> vBlocksSide(50000); + for (unsigned int i=0; i<vBlocksSide.size(); i++) { + vHashSide[i] = i + 50000 + (uint256(1) << 128); // Add 1<<128 to the hashes, so GetLow64() still returns the height. + vBlocksSide[i].nHeight = i + 50000; + vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : &vBlocksMain[49999]; + vBlocksSide[i].phashBlock = &vHashSide[i]; + vBlocksSide[i].BuildSkip(); + BOOST_CHECK_EQUAL((int)vBlocksSide[i].GetBlockHash().GetLow64(), vBlocksSide[i].nHeight); + BOOST_CHECK(vBlocksSide[i].pprev == NULL || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1); + } + + // Build a CChain for the main branch. + CChain chain; + chain.SetTip(&vBlocksMain.back()); + + // Test 100 random starting points for locators. + for (int n=0; n<100; n++) { + int r = insecure_rand() % 150000; + CBlockIndex* tip = (r < 100000) ? &vBlocksMain[r] : &vBlocksSide[r - 100000]; + CBlockLocator locator = chain.GetLocator(tip); + + // The first result must be the block itself, the last one must be genesis. + BOOST_CHECK(locator.vHave.front() == tip->GetBlockHash()); + BOOST_CHECK(locator.vHave.back() == vBlocksMain[0].GetBlockHash()); + + // Entries 1 through 11 (inclusive) go back one step each. + for (unsigned int i = 1; i < 12 && i < locator.vHave.size() - 1; i++) { + BOOST_CHECK_EQUAL(locator.vHave[i].GetLow64(), tip->nHeight - i); + } + + // The further ones (excluding the last one) go back with exponential steps. + unsigned int dist = 2; + for (unsigned int i = 12; i < locator.vHave.size() - 1; i++) { + BOOST_CHECK_EQUAL(locator.vHave[i - 1].GetLow64() - locator.vHave[i].GetLow64(), dist); + dist *= 2; + } + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 2d993e24db..bcd2f75f55 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -18,7 +18,7 @@ #include <boost/filesystem.hpp> #include <boost/test/unit_test.hpp> - +CClientUIInterface uiInterface; CWallet* pwalletMain; extern bool fPrintToConsole; @@ -31,6 +31,7 @@ struct TestingSetup { TestingSetup() { fPrintToDebugLog = false; // don't want to write to debug.log file + SelectParams(CBaseChainParams::MAIN); noui_connect(); #ifdef ENABLE_WALLET bitdb.MakeMock(); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index feba5ee80e..238033f407 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; vector<unsigned char> vch(ch, ch + sizeof(ch) -1); CDataStream stream(vch, SER_DISK, CLIENT_VERSION); - CTransaction tx; + CMutableTransaction tx; stream >> tx; CValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); @@ -224,10 +224,10 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) // paid to a TX_PUBKEY, the second 21 and 22 CENT outputs // paid to a TX_PUBKEYHASH. // -static std::vector<CTransaction> +static std::vector<CMutableTransaction> SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) { - std::vector<CTransaction> dummyTransactions; + std::vector<CMutableTransaction> dummyTransactions; dummyTransactions.resize(2); // Add some keys to the keystore: @@ -261,9 +261,9 @@ BOOST_AUTO_TEST_CASE(test_Get) CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(coinsDummy); - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); + std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); - CTransaction t1; + CMutableTransaction t1; t1.vin.resize(3); t1.vin[0].prevout.hash = dummyTransactions[0].GetHash(); t1.vin[0].prevout.n = 1; @@ -296,9 +296,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(coinsDummy); - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); + std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); - CTransaction t; + CMutableTransaction t; t.vin.resize(1); t.vin[0].prevout.hash = dummyTransactions[0].GetHash(); t.vin[0].prevout.n = 1; diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index 1993358826..3887efbd0d 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -31,20 +31,22 @@ static vector<COutput> vCoins; static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { static int nextLockTime = 0; - CTransaction tx; + CMutableTransaction tx; tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.vout.resize(nInput+1); tx.vout[nInput].nValue = nValue; + if (fIsFromMe) { + // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), + // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() + tx.vin.resize(1); + } CWalletTx* wtx = new CWalletTx(&wallet, tx); if (fIsFromMe) { - // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), - // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() - wtx->vin.resize(1); wtx->fDebitCached = true; wtx->nDebitCached = 1; } - COutput output(wtx, nInput, nAge); + COutput output(wtx, nInput, nAge, true); vCoins.push_back(output); } diff --git a/src/timedata.cpp b/src/timedata.cpp new file mode 100644 index 0000000000..8a095d26dc --- /dev/null +++ b/src/timedata.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "timedata.h" + +#include "netbase.h" +#include "sync.h" +#include "ui_interface.h" +#include "util.h" + +#include <boost/foreach.hpp> + +using namespace std; + +static CCriticalSection cs_nTimeOffset; +static int64_t nTimeOffset = 0; + +// +// "Never go to sea with two chronometers; take one or three." +// Our three time sources are: +// - System clock +// - Median of other nodes clocks +// - The user (asking the user to fix the system clock if the first two disagree) +// +// +int64_t GetTimeOffset() +{ + LOCK(cs_nTimeOffset); + return nTimeOffset; +} + +int64_t GetAdjustedTime() +{ + return GetTime() + GetTimeOffset(); +} + +void AddTimeData(const CNetAddr& ip, int64_t nTime) +{ + int64_t nOffsetSample = nTime - GetTime(); + + LOCK(cs_nTimeOffset); + // Ignore duplicates + static set<CNetAddr> setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + static CMedianFilter<int64_t> vTimeOffsets(200,0); + vTimeOffsets.input(nOffsetSample); + LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + int64_t nMedian = vTimeOffsets.median(); + std::vector<int64_t> vSorted = vTimeOffsets.sorted(); + // Only let other nodes change our time by so much + if (abs64(nMedian) < 70 * 60) + { + nTimeOffset = nMedian; + } + else + { + nTimeOffset = 0; + + static bool fDone; + if (!fDone) + { + // If nobody has a time different than ours but within 5 minutes of ours, give a warning + bool fMatch = false; + BOOST_FOREACH(int64_t nOffset, vSorted) + if (nOffset != 0 && abs64(nOffset) < 5 * 60) + fMatch = true; + + if (!fMatch) + { + fDone = true; + string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly."); + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); + } + } + } + if (fDebug) { + BOOST_FOREACH(int64_t n, vSorted) + LogPrintf("%+d ", n); + LogPrintf("| "); + } + LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); + } +} diff --git a/src/timedata.h b/src/timedata.h new file mode 100644 index 0000000000..0e7bdc2c1f --- /dev/null +++ b/src/timedata.h @@ -0,0 +1,17 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TIMEDATA_H +#define BITCOIN_TIMEDATA_H + +#include <stdint.h> + +class CNetAddr; + +/* Functions to keep track of adjusted P2P time */ +int64_t GetTimeOffset(); +int64_t GetAdjustedTime(); +void AddTimeData(const CNetAddr& ip, int64_t nTime); + +#endif diff --git a/src/txdb.cpp b/src/txdb.cpp index 4eab8525a5..52cd96283e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -6,6 +6,7 @@ #include "txdb.h" #include "core.h" +#include "pow.h" #include "uint256.h" #include <stdint.h> @@ -53,11 +54,11 @@ bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) { return db.WriteBatch(batch); } -bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { +bool CCoinsViewDB::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock) { LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); CLevelDBBatch batch; - for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + for (CCoinsMap::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) BatchWriteCoins(batch, it->first, it->second); if (hashBlock != uint256(0)) BatchWriteHashBestChain(batch, hashBlock); @@ -212,8 +213,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString()); + if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits)) + return error("LoadBlockIndex() : CheckProofOfWork failed: %s", pindexNew->ToString()); pcursor->Next(); } else { diff --git a/src/txdb.h b/src/txdb.h index 7257b0dd21..7d670c2542 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -37,7 +37,7 @@ public: bool HaveCoins(const uint256 &txid); uint256 GetBestBlock(); bool SetBestBlock(const uint256 &hashBlock); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4bf01d4848..a852de5da8 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -80,13 +80,13 @@ public: // Used as belt-and-suspenders check when reading to detect // file corruption - bool AreSane(const std::vector<CFeeRate>& vecFee) + bool AreSane(const std::vector<CFeeRate>& vecFee, const CFeeRate& minRelayFee) { BOOST_FOREACH(CFeeRate fee, vecFee) { if (fee < CFeeRate(0)) return false; - if (fee.GetFee(1000) > CTransaction::minRelayTxFee.GetFee(1000) * 10000) + if (fee.GetFeePerK() > minRelayFee.GetFeePerK() * 10000) return false; } return true; @@ -109,10 +109,10 @@ public: fileout << vecPriority; } - void Read(CAutoFile& filein) { + void Read(CAutoFile& filein, const CFeeRate& minRelayFee) { std::vector<CFeeRate> vecFee; filein >> vecFee; - if (AreSane(vecFee)) + if (AreSane(vecFee, minRelayFee)) feeSamples.insert(feeSamples.end(), vecFee.begin(), vecFee.end()); else throw runtime_error("Corrupt fee value in estimates file."); @@ -141,7 +141,7 @@ private: // nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are // nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc. - void seenTxConfirm(CFeeRate feeRate, double dPriority, int nBlocksAgo) + void seenTxConfirm(const CFeeRate& feeRate, const CFeeRate& minRelayFee, double dPriority, int nBlocksAgo) { // Last entry records "everything else". int nBlocksTruncated = min(nBlocksAgo, (int) history.size() - 1); @@ -149,7 +149,7 @@ private: // We need to guess why the transaction was included in a block-- either // because it is high-priority or because it has sufficient fees. - bool sufficientFee = (feeRate > CTransaction::minRelayTxFee); + bool sufficientFee = (feeRate > minRelayFee); bool sufficientPriority = AllowFree(dPriority); const char* assignedTo = "unassigned"; if (sufficientFee && !sufficientPriority) @@ -177,7 +177,7 @@ public: history.resize(nEntries); } - void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight) + void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight, const CFeeRate minRelayFee) { if (nBlockHeight <= nBestSeenHeight) { @@ -222,7 +222,7 @@ public: // Fees are stored and reported as BTC-per-kb: CFeeRate feeRate(entry->GetFee(), entry->GetTxSize()); double dPriority = entry->GetPriority(entry->GetHeight()); // Want priority when it went IN - seenTxConfirm(feeRate, dPriority, i); + seenTxConfirm(feeRate, minRelayFee, dPriority, i); } } for (size_t i = 0; i < history.size(); i++) { @@ -251,8 +251,13 @@ public: std::sort(sortedFeeSamples.begin(), sortedFeeSamples.end(), std::greater<CFeeRate>()); } - if (sortedFeeSamples.size() == 0) + if (sortedFeeSamples.size() < 11) + { + // Eleven is Gavin's Favorite Number + // ... but we also take a maximum of 10 samples per block so eleven means + // we're getting samples from at least two different blocks return CFeeRate(0); + } int nBucketSize = history.at(nBlocksToConfirm).FeeSamples(); @@ -281,7 +286,7 @@ public: std::sort(sortedPrioritySamples.begin(), sortedPrioritySamples.end(), std::greater<double>()); } - if (sortedPrioritySamples.size() == 0) + if (sortedPrioritySamples.size() < 11) return -1.0; int nBucketSize = history.at(nBlocksToConfirm).PrioritySamples(); @@ -308,7 +313,7 @@ public: } } - void Read(CAutoFile& filein) + void Read(CAutoFile& filein, const CFeeRate& minRelayFee) { filein >> nBestSeenHeight; size_t numEntries; @@ -317,14 +322,14 @@ public: for (size_t i = 0; i < numEntries; i++) { CBlockAverage entry; - entry.Read(filein); + entry.Read(filein, minRelayFee); history.push_back(entry); } } }; -CTxMemPool::CTxMemPool() +CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : minRelayFee(_minRelayFee) { // Sanity checks off by default for performance, because otherwise // accepting transactions becomes O(N^2) where N is the number @@ -415,7 +420,6 @@ void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed) { // Remove transactions which depend on inputs of tx, recursively - list<CTransaction> result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); @@ -441,12 +445,13 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i if (mapTx.count(hash)) entries.push_back(mapTx[hash]); } - minerPolicyEstimator->seenBlock(entries, nBlockHeight); + minerPolicyEstimator->seenBlock(entries, nBlockHeight, minRelayFee); BOOST_FOREACH(const CTransaction& tx, vtx) { std::list<CTransaction> dummy; remove(tx, dummy, false); removeConflicts(tx, conflicts); + ClearPrioritisation(tx.GetHash()); } } @@ -555,7 +560,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return error("CTxMemPool::ReadFeeEstimates() : up-version (%d) fee estimate file", nVersionRequired); LOCK(cs); - minerPolicyEstimator->Read(filein); + minerPolicyEstimator->Read(filein, minRelayFee); } catch (std::exception &e) { LogPrintf("CTxMemPool::ReadFeeEstimates() : unable to read policy estimator data (non-fatal)"); @@ -564,6 +569,34 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return true; } +void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64_t nFeeDelta) +{ + { + LOCK(cs); + std::pair<double, int64_t> &deltas = mapDeltas[hash]; + deltas.first += dPriorityDelta; + deltas.second += nFeeDelta; + } + LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash.c_str(), dPriorityDelta, nFeeDelta); +} + +void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta) +{ + LOCK(cs); + std::map<uint256, std::pair<double, int64_t> >::iterator pos = mapDeltas.find(hash); + if (pos == mapDeltas.end()) + return; + const std::pair<double, int64_t> &deltas = pos->second; + dPriorityDelta += deltas.first; + nFeeDelta += deltas.second; +} + +void CTxMemPool::ClearPrioritisation(const uint256 hash) +{ + LOCK(cs); + mapDeltas.erase(hash); +} + CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } diff --git a/src/txmempool.h b/src/txmempool.h index b2915aa842..41b2c52f39 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -67,12 +67,15 @@ private: unsigned int nTransactionsUpdated; CMinerPolicyEstimator* minerPolicyEstimator; + CFeeRate minRelayFee; // Passed to constructor to avoid dependency on main + public: mutable CCriticalSection cs; std::map<uint256, CTxMemPoolEntry> mapTx; std::map<COutPoint, CInPoint> mapNextTx; + std::map<uint256, std::pair<double, int64_t> > mapDeltas; - CTxMemPool(); + CTxMemPool(const CFeeRate& _minRelayFee); ~CTxMemPool(); /* @@ -95,6 +98,11 @@ public: unsigned int GetTransactionsUpdated() const; void AddTransactionsUpdated(unsigned int n); + /** Affect CreateNewBlock prioritisation of transactions */ + void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, int64_t nFeeDelta); + void ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta); + void ClearPrioritisation(const uint256 hash); + unsigned long size() { LOCK(cs); diff --git a/src/ui_interface.h b/src/ui_interface.h index e1a3e04e12..e9fcd91d41 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -21,7 +21,8 @@ enum ChangeType { CT_NEW, CT_UPDATED, - CT_DELETED + CT_DELETED, + CT_GOT_CONFLICT }; /** Signals for UI communication. */ diff --git a/src/uint256.cpp b/src/uint256.cpp new file mode 100644 index 0000000000..3392f1e9bc --- /dev/null +++ b/src/uint256.cpp @@ -0,0 +1,292 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "uint256.h" +#include "util.h" + +#include <stdio.h> +#include <string.h> + +template<unsigned int BITS> +base_uint<BITS>::base_uint(const std::string& str) +{ + SetHex(str); +} + +template<unsigned int BITS> +base_uint<BITS>::base_uint(const std::vector<unsigned char>& vch) +{ + if (vch.size() != sizeof(pn)) + throw uint_error("Converting vector of wrong size to base_uint"); + memcpy(pn, &vch[0], sizeof(pn)); +} + +template<unsigned int BITS> +base_uint<BITS>& base_uint<BITS>::operator<<=(unsigned int shift) +{ + base_uint<BITS> a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; +} + +template<unsigned int BITS> +base_uint<BITS>& base_uint<BITS>::operator>>=(unsigned int shift) +{ + base_uint<BITS> a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; +} + +template<unsigned int BITS> +base_uint<BITS>& base_uint<BITS>::operator*=(uint32_t b32) +{ + uint64_t carry = 0; + for (int i = 0; i < WIDTH; i++) { + uint64_t n = carry + (uint64_t)b32 * pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; +} + +template<unsigned int BITS> +base_uint<BITS>& base_uint<BITS>::operator*=(const base_uint& b) +{ + base_uint<BITS> a = *this; + *this = 0; + for (int j = 0; j < WIDTH; j++) { + uint64_t carry = 0; + for (int i = 0; i + j < WIDTH; i++) { + uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i]; + pn[i + j] = n & 0xffffffff; + carry = n >> 32; + } + } + return *this; +} + +template<unsigned int BITS> +base_uint<BITS>& base_uint<BITS>::operator/=(const base_uint& b) +{ + base_uint<BITS> div = b; // make a copy, so we can shift. + base_uint<BITS> num = *this; // make a copy, so we can subtract. + *this = 0; // the quotient. + int num_bits = num.bits(); + int div_bits = div.bits(); + if (div_bits == 0) + throw uint_error("Division by zero"); + if (div_bits > num_bits) // the result is certainly 0. + return *this; + int shift = num_bits - div_bits; + div <<= shift; // shift so that div and nun align. + while (shift >= 0) { + if (num >= div) { + num -= div; + pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result. + } + div >>= 1; // shift back. + shift--; + } + // num now contains the remainder of the division. + return *this; +} + +template<unsigned int BITS> +int base_uint<BITS>::CompareTo(const base_uint<BITS>& b) const { + for (int i = WIDTH-1; i >= 0; i--) { + if (pn[i] < b.pn[i]) + return -1; + if (pn[i] > b.pn[i]) + return 1; + } + return 0; +} + +template<unsigned int BITS> +bool base_uint<BITS>::EqualTo(uint64_t b) const { + for (int i = WIDTH-1; i >= 2; i--) { + if (pn[i]) + return false; + } + if (pn[1] != (b >> 32)) + return false; + if (pn[0] != (b & 0xfffffffful)) + return false; + return true; +} + +template<unsigned int BITS> +double base_uint<BITS>::getdouble() const +{ + double ret = 0.0; + double fact = 1.0; + for (int i = 0; i < WIDTH; i++) { + ret += fact * pn[i]; + fact *= 4294967296.0; + } + return ret; +} + +template<unsigned int BITS> +std::string base_uint<BITS>::GetHex() const +{ + char psz[sizeof(pn)*2 + 1]; + for (unsigned int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn)*2); +} + +template<unsigned int BITS> +void base_uint<BITS>::SetHex(const char* psz) +{ + memset(pn,0,sizeof(pn)); + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + const char* pbegin = psz; + while (::HexDigit(*psz) != -1) + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) { + *p1 = ::HexDigit(*psz--); + if (psz >= pbegin) { + *p1 |= ((unsigned char)::HexDigit(*psz--) << 4); + p1++; + } + } +} + +template<unsigned int BITS> +void base_uint<BITS>::SetHex(const std::string& str) +{ + SetHex(str.c_str()); +} + +template<unsigned int BITS> +std::string base_uint<BITS>::ToString() const +{ + return (GetHex()); +} + +template<unsigned int BITS> +unsigned int base_uint<BITS>::bits() const +{ + for (int pos = WIDTH-1; pos >= 0; pos--) { + if (pn[pos]) { + for (int bits = 31; bits > 0; bits--) { + if (pn[pos] & 1<<bits) + return 32*pos + bits + 1; + } + return 32*pos + 1; + } + } + return 0; +} + +// Explicit instantiations for base_uint<160> +template base_uint<160>::base_uint(const std::string&); +template base_uint<160>::base_uint(const std::vector<unsigned char>&); +template base_uint<160>& base_uint<160>::operator<<=(unsigned int); +template base_uint<160>& base_uint<160>::operator>>=(unsigned int); +template base_uint<160>& base_uint<160>::operator*=(uint32_t b32); +template base_uint<160>& base_uint<160>::operator*=(const base_uint<160>& b); +template base_uint<160>& base_uint<160>::operator/=(const base_uint<160>& b); +template int base_uint<160>::CompareTo(const base_uint<160>&) const; +template bool base_uint<160>::EqualTo(uint64_t) const; +template double base_uint<160>::getdouble() const; +template std::string base_uint<160>::GetHex() const; +template std::string base_uint<160>::ToString() const; +template void base_uint<160>::SetHex(const char*); +template void base_uint<160>::SetHex(const std::string&); +template unsigned int base_uint<160>::bits() const; + +// Explicit instantiations for base_uint<256> +template base_uint<256>::base_uint(const std::string&); +template base_uint<256>::base_uint(const std::vector<unsigned char>&); +template base_uint<256>& base_uint<256>::operator<<=(unsigned int); +template base_uint<256>& base_uint<256>::operator>>=(unsigned int); +template base_uint<256>& base_uint<256>::operator*=(uint32_t b32); +template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b); +template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b); +template int base_uint<256>::CompareTo(const base_uint<256>&) const; +template bool base_uint<256>::EqualTo(uint64_t) const; +template double base_uint<256>::getdouble() const; +template std::string base_uint<256>::GetHex() const; +template std::string base_uint<256>::ToString() const; +template void base_uint<256>::SetHex(const char*); +template void base_uint<256>::SetHex(const std::string&); +template unsigned int base_uint<256>::bits() const; + +// This implementation directly uses shifts instead of going +// through an intermediate MPI representation. +uint256& uint256::SetCompact(uint32_t nCompact, bool *pfNegative, bool *pfOverflow) +{ + int nSize = nCompact >> 24; + uint32_t nWord = nCompact & 0x007fffff; + if (nSize <= 3) { + nWord >>= 8*(3-nSize); + *this = nWord; + } else { + *this = nWord; + *this <<= 8*(nSize-3); + } + if (pfNegative) + *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0; + if (pfOverflow) + *pfOverflow = nWord != 0 && ((nSize > 34) || + (nWord > 0xff && nSize > 33) || + (nWord > 0xffff && nSize > 32)); + return *this; +} + +uint32_t uint256::GetCompact(bool fNegative) const +{ + int nSize = (bits() + 7) / 8; + uint32_t nCompact = 0; + if (nSize <= 3) { + nCompact = GetLow64() << 8*(3-nSize); + } else { + uint256 bn = *this >> 8*(nSize-3); + nCompact = bn.GetLow64(); + } + // The 0x00800000 bit denotes the sign. + // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. + if (nCompact & 0x00800000) { + nCompact >>= 8; + nSize++; + } + assert((nCompact & ~0x007fffff) == 0); + assert(nSize < 256); + nCompact |= nSize << 24; + nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0); + return nCompact; +} diff --git a/src/uint256.h b/src/uint256.h index 1acedd14bf..82db7758c9 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -9,18 +9,9 @@ #include <assert.h> #include <stdexcept> #include <stdint.h> -#include <stdio.h> #include <string> -#include <string.h> #include <vector> -extern const signed char p_util_hexdigit[256]; // defined in util.cpp - -inline signed char HexDigit(char c) -{ - return p_util_hexdigit[(unsigned char)c]; -} - class uint_error : public std::runtime_error { public: explicit uint_error(const std::string& str) : std::runtime_error(str) {} @@ -62,17 +53,8 @@ public: pn[i] = 0; } - explicit base_uint(const std::string& str) - { - SetHex(str); - } - - explicit base_uint(const std::vector<unsigned char>& vch) - { - if (vch.size() != sizeof(pn)) - throw uint_error("Converting vector of wrong size to base_uint"); - memcpy(pn, &vch[0], sizeof(pn)); - } + explicit base_uint(const std::string& str); + explicit base_uint(const std::vector<unsigned char>& vch); bool operator!() const { @@ -99,16 +81,7 @@ public: return ret; } - double getdouble() const - { - double ret = 0.0; - double fact = 1.0; - for (int i = 0; i < WIDTH; i++) { - ret += fact * pn[i]; - fact *= 4294967296.0; - } - return ret; - } + double getdouble() const; base_uint& operator=(uint64_t b) { @@ -154,39 +127,8 @@ public: return *this; } - base_uint& operator<<=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i+k+1 < WIDTH && shift != 0) - pn[i+k+1] |= (a.pn[i] >> (32-shift)); - if (i+k < WIDTH) - pn[i+k] |= (a.pn[i] << shift); - } - return *this; - } - - base_uint& operator>>=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i-k-1 >= 0 && shift != 0) - pn[i-k-1] |= (a.pn[i] << (32-shift)); - if (i-k >= 0) - pn[i-k] |= (a.pn[i] >> shift); - } - return *this; - } + base_uint& operator<<=(unsigned int shift); + base_uint& operator>>=(unsigned int shift); base_uint& operator+=(const base_uint& b) { @@ -222,57 +164,9 @@ public: return *this; } - base_uint& operator*=(uint32_t b32) - { - uint64_t carry = 0; - for (int i = 0; i < WIDTH; i++) - { - uint64_t n = carry + (uint64_t)b32 * pn[i]; - pn[i] = n & 0xffffffff; - carry = n >> 32; - } - return *this; - } - - base_uint& operator*=(const base_uint& b) - { - base_uint a = *this; - *this = 0; - for (int j = 0; j < WIDTH; j++) { - uint64_t carry = 0; - for (int i = 0; i + j < WIDTH; i++) { - uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i]; - pn[i + j] = n & 0xffffffff; - carry = n >> 32; - } - } - return *this; - } - - base_uint& operator/=(const base_uint& b) - { - base_uint div = b; // make a copy, so we can shift. - base_uint num = *this; // make a copy, so we can subtract. - *this = 0; // the quotient. - int num_bits = num.bits(); - int div_bits = div.bits(); - if (div_bits == 0) - throw uint_error("Division by zero"); - if (div_bits > num_bits) // the result is certainly 0. - return *this; - int shift = num_bits - div_bits; - div <<= shift; // shift so that div and nun align. - while (shift >= 0) { - if (num >= div) { - num -= div; - pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result. - } - div >>= 1; // shift back. - shift--; - } - // num now contains the remainder of the division. - return *this; - } + base_uint& operator*=(uint32_t b32); + base_uint& operator*=(const base_uint& b); + base_uint& operator/=(const base_uint& b); base_uint& operator++() { @@ -308,27 +202,8 @@ public: return ret; } - int CompareTo(const base_uint& b) const { - for (int i = base_uint::WIDTH-1; i >= 0; i--) { - if (pn[i] < b.pn[i]) - return -1; - if (pn[i] > b.pn[i]) - return 1; - } - return 0; - } - - bool EqualTo(uint64_t b) const { - for (int i = base_uint::WIDTH-1; i >= 2; i--) { - if (pn[i]) - return false; - } - if (pn[1] != (b >> 32)) - return false; - if (pn[0] != (b & 0xfffffffful)) - return false; - return true; - } + int CompareTo(const base_uint& b) const; + bool EqualTo(uint64_t b) const; friend inline const base_uint operator+(const base_uint& a, const base_uint& b) { return base_uint(a) += b; } friend inline const base_uint operator-(const base_uint& a, const base_uint& b) { return base_uint(a) -= b; } @@ -349,53 +224,10 @@ public: friend inline bool operator==(const base_uint& a, uint64_t b) { return a.EqualTo(b); } friend inline bool operator!=(const base_uint& a, uint64_t b) { return !a.EqualTo(b); } - std::string GetHex() const - { - char psz[sizeof(pn)*2 + 1]; - for (unsigned int i = 0; i < sizeof(pn); i++) - sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); - return std::string(psz, psz + sizeof(pn)*2); - } - - void SetHex(const char* psz) - { - memset(pn,0,sizeof(pn)); - - // skip leading spaces - while (isspace(*psz)) - psz++; - - // skip 0x - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - - // hex string to uint - const char* pbegin = psz; - while (::HexDigit(*psz) != -1) - psz++; - psz--; - unsigned char* p1 = (unsigned char*)pn; - unsigned char* pend = p1 + WIDTH * 4; - while (psz >= pbegin && p1 < pend) - { - *p1 = ::HexDigit(*psz--); - if (psz >= pbegin) - { - *p1 |= ((unsigned char)::HexDigit(*psz--) << 4); - p1++; - } - } - } - - void SetHex(const std::string& str) - { - SetHex(str.c_str()); - } - - std::string ToString() const - { - return (GetHex()); - } + std::string GetHex() const; + void SetHex(const char* psz); + void SetHex(const std::string& str); + std::string ToString() const; unsigned char* begin() { @@ -424,19 +256,7 @@ public: // Returns the position of the highest bit set plus one, or zero if the // value is zero. - unsigned int bits() const - { - for (int pos = WIDTH-1; pos >= 0; pos--) { - if (pn[pos]) { - for (int bits = 31; bits > 0; bits--) { - if (pn[pos] & 1<<bits) - return 32*pos + bits + 1; - } - return 32*pos + 1; - } - } - return 0; - } + unsigned int bits() const; uint64_t GetLow64() const { @@ -500,56 +320,8 @@ public: // targets, which are unsigned 256bit quantities. Thus, all the // complexities of the sign bit and using base 256 are probably an // implementation accident. - // - // This implementation directly uses shifts instead of going - // through an intermediate MPI representation. - uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL) - { - int nSize = nCompact >> 24; - uint32_t nWord = nCompact & 0x007fffff; - if (nSize <= 3) - { - nWord >>= 8*(3-nSize); - *this = nWord; - } - else - { - *this = nWord; - *this <<= 8*(nSize-3); - } - if (pfNegative) - *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0; - if (pfOverflow) - *pfOverflow = nWord != 0 && ((nSize > 34) || - (nWord > 0xff && nSize > 33) || - (nWord > 0xffff && nSize > 32)); - return *this; - } - - uint32_t GetCompact(bool fNegative = false) const - { - int nSize = (bits() + 7) / 8; - uint32_t nCompact = 0; - if (nSize <= 3) - nCompact = GetLow64() << 8*(3-nSize); - else - { - uint256 bn = *this >> 8*(nSize-3); - nCompact = bn.GetLow64(); - } - // The 0x00800000 bit denotes the sign. - // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. - if (nCompact & 0x00800000) - { - nCompact >>= 8; - nSize++; - } - assert((nCompact & ~0x007fffff) == 0); - assert(nSize < 256); - nCompact |= nSize << 24; - nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0); - return nCompact; - } + uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL); + uint32_t GetCompact(bool fNegative = false) const; }; #endif diff --git a/src/util.cpp b/src/util.cpp index 30590912ff..91ac8833d5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,10 +5,8 @@ #include "util.h" -#include "chainparams.h" -#include "netbase.h" +#include "chainparamsbase.h" #include "sync.h" -#include "ui_interface.h" #include "uint256.h" #include "version.h" @@ -78,11 +76,12 @@ // See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options // http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION namespace boost { + namespace program_options { std::string to_internal(const std::string&); } -} +} // namespace boost using namespace std; @@ -95,8 +94,8 @@ bool fDaemon = false; bool fServer = false; string strMiscWarning; bool fLogTimestamps = false; +bool fLogIPs = false; volatile bool fReopenDebugLog = false; -CClientUIInterface uiInterface; // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; @@ -122,15 +121,17 @@ public: CRYPTO_set_locking_callback(locking_callback); #ifdef WIN32 - // Seed random number generator with screen scrape and other hardware sources + // Seed OpenSSL PRNG with current contents of the screen RAND_screen(); #endif - // Seed random number generator with performance counter + // Seed OpenSSL PRNG with performance counter RandAddSeed(); } ~CInit() { + // Securely erase the memory used by the PRNG + RAND_cleanup(); // Shutdown OpenSSL library multithreading support CRYPTO_set_locking_callback(NULL); for (int i = 0; i < CRYPTO_num_locks(); i++) @@ -168,16 +169,31 @@ void RandAddSeedPerfmon() #ifdef WIN32 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data - unsigned char pdata[250000]; - memset(pdata, 0, sizeof(pdata)); - unsigned long nSize = sizeof(pdata); - long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + std::vector <unsigned char> vData(250000,0); + long ret = 0; + unsigned long nSize = 0; + const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data + while (true) + { + nSize = vData.size(); + ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize); + if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) + break; + vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially + } RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { - RAND_add(pdata, nSize, nSize/100.0); - OPENSSL_cleanse(pdata, nSize); - LogPrint("rand", "RandAddSeed() %lu bytes\n", nSize); + RAND_add(begin_ptr(vData), nSize, nSize/100.0); + OPENSSL_cleanse(begin_ptr(vData), nSize); + LogPrint("rand", "%s: %lu bytes\n", __func__, nSize); + } else { + static bool warned = false; // Warn only once + if (!warned) + { + LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret); + warned = true; + } } #endif } @@ -405,6 +421,11 @@ const signed char p_util_hexdigit[256] = -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; +signed char HexDigit(char c) +{ + return p_util_hexdigit[(unsigned char)c]; +} + bool IsHex(const string& str) { BOOST_FOREACH(char c, str) @@ -919,7 +940,7 @@ boost::filesystem::path GetDefaultDataDir() #endif } -static boost::filesystem::path pathCached[CChainParams::MAX_NETWORK_TYPES+1]; +static boost::filesystem::path pathCached[CBaseChainParams::MAX_NETWORK_TYPES+1]; static CCriticalSection csPathCached; const boost::filesystem::path &GetDataDir(bool fNetSpecific) @@ -928,8 +949,8 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) LOCK(csPathCached); - int nNet = CChainParams::MAX_NETWORK_TYPES; - if (fNetSpecific) nNet = Params().NetworkID(); + int nNet = CBaseChainParams::MAX_NETWORK_TYPES; + if (fNetSpecific) nNet = BaseParams().NetworkID(); fs::path &path = pathCached[nNet]; @@ -948,7 +969,7 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) path = GetDefaultDataDir(); } if (fNetSpecific) - path /= Params().DataDir(); + path /= BaseParams().DataDir(); fs::create_directories(path); @@ -957,7 +978,7 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) void ClearDatadirCache() { - std::fill(&pathCached[0], &pathCached[CChainParams::MAX_NETWORK_TYPES+1], + std::fill(&pathCached[0], &pathCached[CBaseChainParams::MAX_NETWORK_TYPES+1], boost::filesystem::path()); } @@ -1141,15 +1162,15 @@ void ShrinkDebugFile() if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000) { // Restart the file with some of the end - char pch[200000]; - fseek(file, -sizeof(pch), SEEK_END); - int nBytes = fread(pch, 1, sizeof(pch), file); + std::vector <char> vch(200000,0); + fseek(file, -vch.size(), SEEK_END); + int nBytes = fread(begin_ptr(vch), 1, vch.size(), file); fclose(file); file = fopen(pathLog.string().c_str(), "w"); if (file) { - fwrite(pch, 1, nBytes, file); + fwrite(begin_ptr(vch), 1, nBytes, file); fclose(file); } } @@ -1157,13 +1178,6 @@ void ShrinkDebugFile() fclose(file); } -// -// "Never go to sea with two chronometers; take one or three." -// Our three time sources are: -// - System clock -// - Median of other nodes clocks -// - The user (asking the user to fix the system clock if the first two disagree) -// static int64_t nMockTime = 0; // For unit testing int64_t GetTime() @@ -1178,75 +1192,6 @@ void SetMockTime(int64_t nMockTimeIn) nMockTime = nMockTimeIn; } -static CCriticalSection cs_nTimeOffset; -static int64_t nTimeOffset = 0; - -int64_t GetTimeOffset() -{ - LOCK(cs_nTimeOffset); - return nTimeOffset; -} - -int64_t GetAdjustedTime() -{ - return GetTime() + GetTimeOffset(); -} - -void AddTimeData(const CNetAddr& ip, int64_t nTime) -{ - int64_t nOffsetSample = nTime - GetTime(); - - LOCK(cs_nTimeOffset); - // Ignore duplicates - static set<CNetAddr> setKnown; - if (!setKnown.insert(ip).second) - return; - - // Add data - static CMedianFilter<int64_t> vTimeOffsets(200,0); - vTimeOffsets.input(nOffsetSample); - LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); - if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) - { - int64_t nMedian = vTimeOffsets.median(); - std::vector<int64_t> vSorted = vTimeOffsets.sorted(); - // Only let other nodes change our time by so much - if (abs64(nMedian) < 70 * 60) - { - nTimeOffset = nMedian; - } - else - { - nTimeOffset = 0; - - static bool fDone; - if (!fDone) - { - // If nobody has a time different than ours but within 5 minutes of ours, give a warning - bool fMatch = false; - BOOST_FOREACH(int64_t nOffset, vSorted) - if (nOffset != 0 && abs64(nOffset) < 5 * 60) - fMatch = true; - - if (!fMatch) - { - fDone = true; - string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly."); - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); - } - } - } - if (fDebug) { - BOOST_FOREACH(int64_t n, vSorted) - LogPrintf("%+d ", n); - LogPrintf("| "); - } - LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); - } -} - uint32_t insecure_rand_Rz = 11; uint32_t insecure_rand_Rw = 11; void seed_insecure_rand(bool fDeterministic) diff --git a/src/util.h b/src/util.h index da1810a3d3..60db71bfd0 100644 --- a/src/util.h +++ b/src/util.h @@ -7,7 +7,7 @@ #define BITCOIN_UTIL_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "compat.h" @@ -32,7 +32,6 @@ #include <boost/filesystem/path.hpp> #include <boost/thread.hpp> -class CNetAddr; class uint256; static const int64_t COIN = 100000000; @@ -101,6 +100,7 @@ extern bool fPrintToDebugLog; extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; +extern bool fLogIPs; extern volatile bool fReopenDebugLog; void RandAddSeed(); @@ -157,6 +157,7 @@ bool ParseMoney(const char* pszIn, int64_t& nRet); std::string SanitizeString(const std::string& str); std::vector<unsigned char> ParseHex(const char* psz); std::vector<unsigned char> ParseHex(const std::string& str); +signed char HexDigit(char c); bool IsHex(const std::string& str); std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL); std::string DecodeBase64(const std::string& str); @@ -191,11 +192,8 @@ uint64_t GetRand(uint64_t nMax); uint256 GetRandHash(); int64_t GetTime(); void SetMockTime(int64_t nMockTimeIn); -int64_t GetAdjustedTime(); -int64_t GetTimeOffset(); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); -void AddTimeData(const CNetAddr& ip, int64_t nTime); void runCommand(std::string strCommand); diff --git a/src/wallet.cpp b/src/wallet.cpp index 400c966a95..a54494f935 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -9,6 +9,7 @@ #include "checkpoints.h" #include "coincontrol.h" #include "net.h" +#include "timedata.h" #include <boost/algorithm/string/replace.hpp> #include <openssl/rand.h> @@ -17,8 +18,12 @@ using namespace std; // Settings CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +unsigned int nTxConfirmTarget = 1; bool bSpendZeroConfChange = true; +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ +CFeeRate CWallet::minTxFee = CFeeRate(10000); // Override with -mintxfee + ////////////////////////////////////////////////////////////////////////////// // // mapWallet @@ -144,6 +149,21 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return CCryptoKeyStore::AddCScript(redeemScript); } +bool CWallet::AddWatchOnly(const CScript &dest) +{ + if (!CCryptoKeyStore::AddWatchOnly(dest)) + return false; + nTimeFirstKey = 1; // No birthday information for watch-only keys. + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteWatchOnly(dest); +} + +bool CWallet::LoadWatchOnly(const CScript &dest) +{ + return CCryptoKeyStore::AddWatchOnly(dest); +} + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { CCrypter crypter; @@ -255,7 +275,7 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } -set<uint256> CWallet::GetConflicts(const uint256& txid) const +set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const { set<uint256> result; AssertLockHeld(cs_wallet); @@ -273,7 +293,8 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - result.insert(it->second); + if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second))) + result.insert(it->second); } return result; } @@ -302,6 +323,7 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose @@ -508,8 +530,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) { if (mapBlockIndex.count(wtxIn.hashBlock)) { - unsigned int latestNow = wtx.nTimeReceived; - unsigned int latestEntry = 0; + int64_t latestNow = wtx.nTimeReceived; + int64_t latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future int64_t latestTolerated = latestNow + 300; @@ -540,7 +562,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) } } - unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; + int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else @@ -587,6 +609,28 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + // Notifications for existing transactions that now have conflicts with this one + if (fInsertedNew) + { + BOOST_FOREACH(const uint256& conflictHash, wtxIn.GetConflicts(false)) + { + CWalletTx& txConflict = mapWallet[conflictHash]; + NotifyTransactionChanged(this, conflictHash, CT_UPDATED); //Updates UI table + if (IsFromMe(txConflict) || IsMine(txConflict)) + { + NotifyTransactionChanged(this, conflictHash, CT_GOT_CONFLICT); //Throws dialog + // external respend notify + std::string strCmd = GetArg("-respendnotify", ""); + if (!strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::replace_all(strCmd, "%t", conflictHash.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } + } + } + // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); @@ -603,13 +647,18 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Add a transaction to the wallet, or update it. // pblock is optional, but should be provided if the transaction is known to be in a block. // If fUpdate is true, existing transactions will be updated. -bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { AssertLockHeld(cs_wallet); - bool fExisted = mapWallet.count(hash); + bool fExisted = mapWallet.count(tx.GetHash()); if (fExisted && !fUpdate) return false; - if (fExisted || IsMine(tx) || IsFromMe(tx)) + + bool fIsConflicting = IsConflicting(tx); + if (fIsConflicting) + nConflictsReceived++; + + if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting) { CWalletTx wtx(this,tx); // Get merkle branch if transaction was found in a block @@ -621,10 +670,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& return false; } -void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) +void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) { LOCK2(cs_main, cs_wallet); - if (!AddToWalletIfInvolvingMe(hash, tx, pblock, true)) + if (!AddToWalletIfInvolvingMe(tx, pblock, true)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -650,7 +699,7 @@ void CWallet::EraseFromWallet(const uint256 &hash) } -bool CWallet::IsMine(const CTxIn &txin) const +isminetype CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); @@ -659,14 +708,13 @@ bool CWallet::IsMine(const CTxIn &txin) const { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) - if (IsMine(prev.vout[txin.prevout.n])) - return true; + return IsMine(prev.vout[txin.prevout.n]); } } - return false; + return ISMINE_NO; } -int64_t CWallet::GetDebit(const CTxIn &txin) const +int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { LOCK(cs_wallet); @@ -675,7 +723,7 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) - if (IsMine(prev.vout[txin.prevout.n])) + if (IsMine(prev.vout[txin.prevout.n]) & filter) return prev.vout[txin.prevout.n].nValue; } } @@ -684,17 +732,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const bool CWallet::IsChange(const CTxOut& txout) const { - CTxDestination address; - // TODO: fix handling of 'change' outputs. The assumption is that any - // payment to a TX_PUBKEYHASH that is mine but isn't in the address book + // payment to a script that is ours, but is not in the address book // is change. That assumption is likely to break when we implement multisignature // wallets that return change back into a multi-signature-protected address; // a better way of identifying which outputs are 'the send' and which are // 'the change' will need to be implemented (maybe extend CWalletTx to remember // which output, if any, was change). - if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) + if (::IsMine(*this, txout.scriptPubKey)) { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + return true; + LOCK(cs_wallet); if (!mapAddressBook.count(address)) return true; @@ -748,7 +798,7 @@ int CWalletTx::GetRequestCount() const } void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, - list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount) const + list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const { nFee = 0; listReceived.clear(); @@ -756,7 +806,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, strSentAccount = strFromAccount; // Compute fee: - int64_t nDebit = GetDebit(); + int64_t nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { int64_t nValueOut = GetValueOut(); @@ -766,7 +816,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { - bool fIsMine; + isminetype fIsMine = pwallet->IsMine(txout); + // Only need to handle txouts if AT LEAST one of these is true: // 1) they debit from us (sent) // 2) the output is to us (received) @@ -775,9 +826,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, // Don't report 'change' txouts if (pwallet->IsChange(txout)) continue; - fIsMine = pwallet->IsMine(txout); } - else if (!(fIsMine = pwallet->IsMine(txout))) + else if (!(fIsMine & filter)) continue; // In either case, we need to get the destination address @@ -801,7 +851,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, } void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, - int64_t& nSent, int64_t& nFee) const + int64_t& nSent, int64_t& nFee, const isminefilter& filter) const { nReceived = nSent = nFee = 0; @@ -809,7 +859,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, string strSentAccount; list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listSent; - GetAmounts(listReceived, listSent, allFee, strSentAccount); + GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (strAccount == strSentAccount) { @@ -855,7 +905,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) - while (pindex && nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) + while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) pindex = chainActive.Next(pindex); ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup @@ -870,7 +920,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ReadBlockFromDisk(block, pindex); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = chainActive.Next(pindex); @@ -895,7 +945,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && nDepth < 0) + if (!wtx.IsCoinBase() && nDepth < 0 && (IsMine(wtx) || IsFromMe(wtx))) { // Try to add to memory pool LOCK(mempool.cs); @@ -909,20 +959,19 @@ void CWalletTx::RelayWalletTransaction() if (!IsCoinBase()) { if (GetDepthInMainChain() == 0) { - uint256 hash = GetHash(); - LogPrintf("Relaying wtx %s\n", hash.ToString()); - RelayTransaction((CTransaction)*this, hash); + LogPrintf("Relaying wtx %s\n", GetHash().ToString()); + RelayTransaction((CTransaction)*this); } } } -set<uint256> CWalletTx::GetConflicts() const +set<uint256> CWalletTx::GetConflicts(bool includeEquivalent) const { set<uint256> result; if (pwallet != NULL) { uint256 myHash = GetHash(); - result = pwallet->GetConflicts(myHash); + result = pwallet->GetConflicts(myHash, includeEquivalent); result.erase(myHash); } return result; @@ -1022,13 +1071,58 @@ int64_t CWallet::GetImmatureBalance() const return nTotal; } -// populate vCoins with vector of spendable COutputs +int64_t CWallet::GetWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + + return nTotal; +} + +int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetImmatureWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + +// populate vCoins with vector of available COutputs. void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const uint256& wtxid = it->first; @@ -1048,10 +1142,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) && + isminetype mine = IsMine(pcoin->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, nDepth)); + vCoins.push_back(COutput(pcoin, i, nDepth, mine & ISMINE_SPENDABLE)); } } } @@ -1118,11 +1213,14 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - BOOST_FOREACH(COutput output, vCoins) + BOOST_FOREACH(const COutput &output, vCoins) { + if (!output.fSpendable) + continue; + const CWalletTx *pcoin = output.tx; - if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) continue; int i = output.i; @@ -1211,6 +1309,8 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign { BOOST_FOREACH(const COutput& out, vCoins) { + if(!out.fSpendable) + continue; nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } @@ -1244,7 +1344,9 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, return false; } + wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); + CMutableTransaction txNew; { LOCK2(cs_main, cs_wallet); @@ -1252,8 +1354,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, nFeeRet = payTxFee.GetFeePerK(); while (true) { - wtxNew.vin.clear(); - wtxNew.vout.clear(); + txNew.vin.clear(); + txNew.vout.clear(); wtxNew.fFromMe = true; int64_t nTotalValue = nValue + nFeeRet; @@ -1262,12 +1364,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) { CTxOut txout(s.second, s.first); - if (txout.IsDust(CTransaction::minRelayTxFee)) + if (txout.IsDust(::minRelayTxFee)) { strFailReason = _("Transaction amount too small"); return false; } - wtxNew.vout.push_back(txout); + txNew.vout.push_back(txout); } // Choose coins to use @@ -1323,7 +1425,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(CTransaction::minRelayTxFee)) + if (newTxOut.IsDust(::minRelayTxFee)) { nFeeRet += nChange; reservekey.ReturnKey(); @@ -1331,8 +1433,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, else { // Insert change txn at random position: - vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); - wtxNew.vout.insert(position, newTxOut); + vector<CTxOut>::iterator position = txNew.vout.begin()+GetRandInt(txNew.vout.size()+1); + txNew.vout.insert(position, newTxOut); } } else @@ -1340,17 +1442,20 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, // Fill vin BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); // Sign int nIn = 0; BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) + if (!SignSignature(*this, *coin.first, txNew, nIn++)) { strFailReason = _("Signing transaction failed"); return false; } + // Embed the constructed transaction data in wtxNew. + *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); + // Limit size unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_STANDARD_TX_SIZE) @@ -1360,19 +1465,31 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, } dPriority = wtxNew.ComputePriority(dPriority, nBytes); - // Check that enough fee is included - int64_t nPayFee = payTxFee.GetFee(nBytes); - bool fAllowFree = AllowFree(dPriority); - int64_t nMinFee = GetMinFee(wtxNew, nBytes, fAllowFree, GMF_SEND); - if (nFeeRet < max(nPayFee, nMinFee)) + int64_t nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + + if (nFeeRet >= nFeeNeeded) + break; // Done, enough fee included. + + // Too big to send for free? Include more fee and try again: + if (nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE) { - nFeeRet = max(nPayFee, nMinFee); + nFeeRet = nFeeNeeded; continue; } - wtxNew.fTimeReceivedIsTxTime = true; + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); + // Not enough mempool history to estimate: use hard-coded AllowFree. + if (dPriorityNeeded <= 0 && AllowFree(dPriority)) + break; - break; + // Small enough, and priority high enough, to send for free + if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) + break; + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; } } } @@ -1437,18 +1554,29 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew) +string CWallet::SendMoney(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew) { - CReserveKey reservekey(this); - int64_t nFeeRequired; + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue > GetBalance()) + return _("Insufficient funds"); + string strError; if (IsLocked()) { - string strError = _("Error: Wallet locked, unable to create transaction!"); + strError = _("Error: Wallet locked, unable to create transaction!"); LogPrintf("SendMoney() : %s", strError); return strError; } - string strError; + + // Parse Bitcoin address + CScript scriptPubKey; + scriptPubKey.SetDestination(address); + + // Create and send the transaction + CReserveKey reservekey(this); + int64_t nFeeRequired; if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) { if (nValue + nFeeRequired > GetBalance()) @@ -1456,7 +1584,6 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe LogPrintf("SendMoney() : %s\n", strError); return strError; } - if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); @@ -1465,19 +1592,18 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe -string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew) +int64_t CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) { - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue > GetBalance()) - return _("Insufficient funds"); - - // Parse Bitcoin address - CScript scriptPubKey; - scriptPubKey.SetDestination(address); - - return SendMoney(scriptPubKey, nValue, wtxNew); + // payTxFee is user-set "I want to pay this much" + int64_t nFeeNeeded = payTxFee.GetFee(nTxBytes); + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == 0) + nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); + // ... unless we don't have enough mempool data, in which case fall + // back to a hard-coded fee + if (nFeeNeeded == 0) + nFeeNeeded = minTxFee.GetFee(nTxBytes); + return nFeeNeeded; } @@ -1511,11 +1637,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } -DBErrors CWallet::ZapWalletTx() +DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) { if (!fFileBacked) return DB_LOAD_OK; - DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); if (nZapWalletTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) @@ -1745,7 +1871,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() continue; int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? 0 : 1)) + if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) @@ -2028,7 +2154,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { // Extract block timestamps for those keys for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) - mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off + mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off } bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) diff --git a/src/wallet.h b/src/wallet.h index 7df656fc25..ab9550a984 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -25,12 +25,15 @@ // Settings extern CFeeRate payTxFee; +extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; // -paytxfee default static const int64_t DEFAULT_TRANSACTION_FEE = 0; // -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB static const int nHighTransactionFeeWarning = 0.01 * COIN; +// Largest (in bytes) free transaction we're willing to create +static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; class CAccountingEntry; class CCoinControl; @@ -141,6 +144,9 @@ public: MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; + // Increment to cause UI refresh, similar to new block + int64_t nConflictsReceived; + CWallet() { SetNull(); @@ -163,6 +169,7 @@ public: nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; + nConflictsReceived = 0; } std::map<uint256, CWalletTx> mapWallet; @@ -222,6 +229,11 @@ public: /// Look up a destination data tuple in the store, return true if found false otherwise bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const; + // Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnly(const CScript &dest); + // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) + bool LoadWatchOnly(const CScript &dest); + bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); @@ -244,8 +256,8 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false); - void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); - bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); + void SyncTransaction(const CTransaction& tx, const CBlock* pblock); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256 &hash); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); @@ -253,13 +265,18 @@ public: int64_t GetBalance() const; int64_t GetUnconfirmedBalance() const; int64_t GetImmatureBalance() const; + int64_t GetWatchOnlyBalance() const; + int64_t GetUnconfirmedWatchOnlyBalance() const; + int64_t GetImmatureWatchOnlyBalance() const; bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew); - std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew); + std::string SendMoney(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew); + + static CFeeRate minTxFee; + static int64_t GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); bool NewKeyPool(); bool TopUpKeyPool(unsigned int kpSize = 0); @@ -275,17 +292,17 @@ public: std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const; - bool IsMine(const CTxIn& txin) const; - int64_t GetDebit(const CTxIn& txin) const; - bool IsMine(const CTxOut& txout) const + isminetype IsMine(const CTxIn& txin) const; + int64_t GetDebit(const CTxIn& txin, const isminefilter& filter) const; + isminetype IsMine(const CTxOut& txout) const { return ::IsMine(*this, txout.scriptPubKey); } - int64_t GetCredit(const CTxOut& txout) const + int64_t GetCredit(const CTxOut& txout, const isminefilter& filter) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); - return (IsMine(txout) ? txout.nValue : 0); + return ((IsMine(txout) & filter) ? txout.nValue : 0); } bool IsChange(const CTxOut& txout) const; int64_t GetChange(const CTxOut& txout) const @@ -301,27 +318,34 @@ public: return true; return false; } - bool IsFromMe(const CTransaction& tx) const + bool IsFromMe(const CTransaction& tx) const // should probably be renamed to IsRelevantToMe { - return (GetDebit(tx) > 0); + return (GetDebit(tx, ISMINE_ALL) > 0); } - int64_t GetDebit(const CTransaction& tx) const + bool IsConflicting(const CTransaction& tx) const + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (mapTxSpends.count(txin.prevout)) + return true; + return false; + } + int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const { int64_t nDebit = 0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - nDebit += GetDebit(txin); + nDebit += GetDebit(txin, filter); if (!MoneyRange(nDebit)) throw std::runtime_error("CWallet::GetDebit() : value out of range"); } return nDebit; } - int64_t GetCredit(const CTransaction& tx) const + int64_t GetCredit(const CTransaction& tx, const isminefilter& filter) const { int64_t nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - nCredit += GetCredit(txout); + nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); } @@ -341,7 +365,7 @@ public: void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); - DBErrors ZapWalletTx(); + DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); @@ -377,7 +401,7 @@ public: int GetVersion() { LOCK(cs_wallet); return nWalletVersion; } // Get wallet transactions that conflict with given transaction (spend same outputs) - std::set<uint256> GetConflicts(const uint256& txid) const; + std::set<uint256> GetConflicts(const uint256& txid, bool includeEquivalent) const; /** Address book entry changed. * @note called with lock cs_wallet held. @@ -467,11 +491,19 @@ public: mutable bool fCreditCached; mutable bool fImmatureCreditCached; mutable bool fAvailableCreditCached; + mutable bool fWatchDebitCached; + mutable bool fWatchCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; mutable bool fChangeCached; mutable int64_t nDebitCached; mutable int64_t nCreditCached; mutable int64_t nImmatureCreditCached; mutable int64_t nAvailableCreditCached; + mutable int64_t nWatchDebitCached; + mutable int64_t nWatchCreditCached; + mutable int64_t nImmatureWatchCreditCached; + mutable int64_t nAvailableWatchCreditCached; mutable int64_t nChangeCached; CWalletTx() @@ -508,11 +540,19 @@ public: fCreditCached = false; fImmatureCreditCached = false; fAvailableCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; fChangeCached = false; nDebitCached = 0; nCreditCached = 0; nImmatureCreditCached = 0; nAvailableCreditCached = 0; + nWatchDebitCached = 0; + nWatchCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; nChangeCached = 0; nOrderPos = -1; } @@ -565,6 +605,10 @@ public: { fCreditCached = false; fAvailableCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; fDebitCached = false; fChangeCached = false; } @@ -575,15 +619,36 @@ public: MarkDirty(); } - int64_t GetDebit() const + // filter decides which addresses will count towards the debit + int64_t GetDebit(const isminefilter& filter) const { if (vin.empty()) return 0; - if (fDebitCached) - return nDebitCached; - nDebitCached = pwallet->GetDebit(*this); - fDebitCached = true; - return nDebitCached; + + int64_t debit = 0; + if(filter & ISMINE_SPENDABLE) + { + if (fDebitCached) + debit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + fDebitCached = true; + debit += nDebitCached; + } + } + if(filter & ISMINE_WATCH_ONLY) + { + if(fWatchDebitCached) + debit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + fWatchDebitCached = true; + debit += nWatchDebitCached; + } + } + return debit; } int64_t GetCredit(bool fUseCache=true) const @@ -595,7 +660,7 @@ public: // GetBalance can assume transactions in mapWallet won't change if (fUseCache && fCreditCached) return nCreditCached; - nCreditCached = pwallet->GetCredit(*this); + nCreditCached = pwallet->GetCredit(*this, ISMINE_ALL); fCreditCached = true; return nCreditCached; } @@ -606,7 +671,7 @@ public: { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; - nImmatureCreditCached = pwallet->GetCredit(*this); + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } @@ -633,7 +698,7 @@ public: if (!pwallet->IsSpent(hashTx, i)) { const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout); + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } @@ -644,6 +709,48 @@ public: return nCredit; } + int64_t GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; + } + + int64_t GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const + { + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + int64_t nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; + } int64_t GetChange() const { @@ -655,14 +762,14 @@ public: } void GetAmounts(std::list<std::pair<CTxDestination, int64_t> >& listReceived, - std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount) const; + std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const; void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived, - int64_t& nSent, int64_t& nFee) const; + int64_t& nSent, int64_t& nFee, const isminefilter& filter) const; - bool IsFromMe() const + bool IsFromMe(const isminefilter& filter) const { - return (GetDebit() > 0); + return (GetDebit(filter) > 0); } bool IsTrusted() const @@ -675,7 +782,7 @@ public: return true; if (nDepth < 0) return false; - if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit return false; // Trusted if all inputs are from us and are in the mempool: @@ -686,7 +793,7 @@ public: if (parent == NULL) return false; const CTxOut& parentOut = parent->vout[txin.prevout.n]; - if (!pwallet->IsMine(parentOut)) + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false; } return true; @@ -699,7 +806,7 @@ public: void RelayWalletTransaction(); - std::set<uint256> GetConflicts() const; + std::set<uint256> GetConflicts(bool includeEquivalent=true) const; }; @@ -711,10 +818,11 @@ public: const CWalletTx *tx; int i; int nDepth; + bool fSpendable; - COutput(const CWalletTx *txIn, int iIn, int nDepthIn) + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) { - tx = txIn; i = iIn; nDepth = nDepthIn; + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; } std::string ToString() const diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 80e9dded5f..a95baf83d0 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -112,6 +112,12 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } +bool CWalletDB::WriteWatchOnly(const CScript &dest) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("watchs"), dest), '1'); +} + bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; @@ -404,6 +410,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, wss.fAnyUnordered = true; } } + else if (strType == "watchs") + { + CScript script; + ssKey >> script; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadWatchOnly(script); + + // Watch-only addresses have no birthday information for now, + // so set the wallet birthday to the beginning of time. + pwallet->nTimeFirstKey = 1; + } else if (strType == "key" || strType == "wkey") { CPubKey vchPubKey; @@ -680,7 +699,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } -DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) { pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; @@ -725,7 +744,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) uint256 hash; ssKey >> hash; + CWalletTx wtx; + ssValue >> wtx; + vTxHash.push_back(hash); + vWtx.push_back(wtx); } } pcursor->close(); @@ -743,11 +766,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) return result; } -DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet) +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx) { // build list of wallet TXs vector<uint256> vTxHash; - DBErrors err = FindWalletTx(pwallet, vTxHash); + DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); if (err != DB_LOAD_OK) return err; diff --git a/src/walletdb.h b/src/walletdb.h index 3bfb436050..58b4571b16 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -7,6 +7,7 @@ #include "db.h" #include "key.h" +#include "keystore.h" #include <list> #include <stdint.h> @@ -93,6 +94,8 @@ public: bool WriteCScript(const uint160& hash, const CScript& redeemScript); + bool WriteWatchOnly(const CScript &script); + bool WriteBestBlock(const CBlockLocator& locator); bool ReadBestBlock(CBlockLocator& locator); @@ -122,8 +125,8 @@ public: DBErrors ReorderTransactions(CWallet*); DBErrors LoadWallet(CWallet* pwallet); - DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash); - DBErrors ZapWalletTx(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); + DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx); static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); }; |