aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/debian/manpages/bitcoin-qt.13
-rw-r--r--contrib/verifysfbinaries/README.md2
-rwxr-xr-xcontrib/verifysfbinaries/verify.sh8
-rw-r--r--doc/build-osx.md20
-rw-r--r--doc/build-unix.md11
-rw-r--r--doc/coding.md75
-rw-r--r--doc/release-notes.md46
-rw-r--r--doc/release-notes/release-notes-0.9.2.1.md207
-rw-r--r--doc/release-notes/release-notes-0.9.2.md207
-rwxr-xr-xqa/rpc-tests/rpcbind_test.py22
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/base58.cpp4
-rw-r--r--src/bitcoin-cli-res.rc2
-rw-r--r--src/bloom.cpp7
-rw-r--r--src/bloom.h2
-rw-r--r--src/checkpoints.cpp7
-rw-r--r--src/checkpoints.h7
-rw-r--r--src/core.cpp16
-rw-r--r--src/core.h3
-rw-r--r--src/init.cpp2
-rw-r--r--src/init.h2
-rw-r--r--src/key.cpp3
-rw-r--r--src/main.cpp260
-rw-r--r--src/main.h18
-rw-r--r--src/net.h3
-rw-r--r--src/qt/forms/sendcoinsentry.ui2
-rw-r--r--src/qt/forms/signverifymessagedialog.ui4
-rw-r--r--src/qt/guiconstants.h4
-rw-r--r--src/qt/guiutil.cpp4
-rw-r--r--src/qt/signverifymessagedialog.cpp1
-rw-r--r--src/qt/transactionfilterproxy.cpp4
-rw-r--r--src/qt/transactionrecord.cpp10
-rw-r--r--src/qt/transactionrecord.h24
-rw-r--r--src/qt/transactiontablemodel.cpp18
-rw-r--r--src/qt/utilitydialog.cpp1
-rw-r--r--src/qt/walletmodel.cpp8
-rw-r--r--src/rpcblockchain.cpp2
-rw-r--r--src/rpcmining.cpp2
-rw-r--r--src/rpcmisc.cpp2
-rw-r--r--src/rpcnet.cpp1
-rw-r--r--src/rpcprotocol.cpp39
-rw-r--r--src/rpcprotocol.h6
-rw-r--r--src/rpcserver.cpp153
-rw-r--r--src/rpcserver.h11
-rw-r--r--src/rpcwallet.cpp22
-rw-r--r--src/script.cpp5
-rw-r--r--src/test/bloom_tests.cpp4
-rw-r--r--src/test/script_P2SH_tests.cpp118
-rw-r--r--src/test/skiplist_tests.cpp45
-rw-r--r--src/txmempool.cpp1
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/uint256.cpp292
-rw-r--r--src/uint256.h262
-rw-r--r--src/util.cpp8
-rw-r--r--src/util.h1
-rw-r--r--src/wallet.cpp41
-rw-r--r--src/wallet.h15
58 files changed, 1517 insertions, 535 deletions
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/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..1e38326d86 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,8 +41,6 @@ Instructions: MacPorts
### Install dependencies
-Installing the dependencies using MacPorts is very straightforward.
-
sudo port install boost db48@+no_java openssl miniupnpc autoconf pkgconfig automake
Optional: install Qt4
@@ -80,7 +75,7 @@ Note: After you have installed the dependencies, you should check that the Homeb
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/release-notes.md b/doc/release-notes.md
index 9272d427cd..3a4079e437 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -19,3 +19,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/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/src/Makefile.am b/src/Makefile.am
index 3643e60201..9c7b294d35 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -192,6 +192,7 @@ libbitcoin_util_a_SOURCES = \
chainparamsbase.cpp \
rpcprotocol.cpp \
sync.cpp \
+ uint256.cpp \
util.cpp \
version.cpp \
compat/glibc_sanity.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 4dab1773f4..8685452c7b 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -47,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 \
diff --git a/src/base58.cpp b/src/base58.cpp
index 1bd64684e5..c9e91beef1 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -186,6 +186,7 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const {
}
namespace {
+
class CBitcoinAddressVisitor : public boost::static_visitor<bool> {
private:
CBitcoinAddress *addr;
@@ -196,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..a4a0c47ea5 100644
--- a/src/bitcoin-cli-res.rc
+++ b/src/bitcoin-cli-res.rc
@@ -5,7 +5,7 @@
#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"
+#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin Core Developers"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 26e366179c..85a2ddc189 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -94,6 +94,13 @@ 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;
diff --git a/src/bloom.h b/src/bloom.h
index 956bead87f..d0caf9e9fa 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -78,6 +78,8 @@ 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;
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 75ac418916..80479b47fb 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
@@ -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/core.cpp b/src/core.cpp
index 6c5ee1c0f8..ca28624529 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -119,6 +119,22 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
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 27fda95552..8606831575 100644
--- a/src/core.h
+++ b/src/core.h
@@ -256,6 +256,9 @@ public:
return hash;
}
+ // True if only scriptSigs are different
+ bool IsEquivalentTo(const CTransaction& tx) const;
+
// Return sum of txouts.
int64_t GetValueOut() const;
// GetValueIn() is a method on CCoinsViewCache, because
diff --git a/src/init.cpp b/src/init.cpp
index c7170b0f2d..3d0c03328f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -260,6 +260,7 @@ std::string HelpMessage(HelpMessageMode mode)
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 += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\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
@@ -1175,6 +1176,7 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif
+ RegisterInternalSignals();
StartNode(threadGroup);
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 96b1ac439c..784085da34 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -377,8 +377,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 &&
diff --git a/src/main.cpp b/src/main.cpp
index e06c519ee2..04d9523e26 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -7,6 +7,7 @@
#include "addrman.h"
#include "alert.h"
+#include "bloom.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -72,6 +73,7 @@ const string strMessageMagic = "Bitcoin Signed Message:\n";
// Internal stuff
namespace {
+
struct CBlockIndexWorkComparator
{
bool operator()(CBlockIndex *pa, CBlockIndex *pb) {
@@ -120,7 +122,12 @@ namespace {
};
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
map<uint256, pair<NodeId, list<uint256>::iterator> > mapBlocksToDownload;
-}
+
+} // anon namespace
+
+// Forward reference functions defined here:
+static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
+static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter);
//////////////////////////////////////////////////////////////////////////////
//
@@ -130,6 +137,7 @@ namespace {
// These functions dispatch to one or all registered wallets
namespace {
+
struct CMainSignals {
// Notifies listeners of updated transaction data (transaction, and optionally the block it is found in.
boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction;
@@ -143,9 +151,25 @@ struct CMainSignals {
boost::signals2::signal<void (const uint256 &)> Inventory;
// Tells listeners to broadcast their data.
boost::signals2::signal<void ()> Broadcast;
+ // Notifies listeners of detection of a double-spent transaction. Arguments are outpoint that is
+ // double-spent, first transaction seen, double-spend transaction, and whether the second double-spend
+ // transaction was first seen in a block.
+ // Note: only notifies if the previous transaction is in the memory pool; if previous transction was in a block,
+ // then the double-spend simply fails when we try to lookup the inputs in the current UTXO set.
+ boost::signals2::signal<void (const COutPoint&, const CTransaction&, bool)> DetectedDoubleSpend;
} g_signals;
+
+} // anon namespace
+
+void RegisterInternalSignals() {
+ static CBloomFilter doubleSpendFilter;
+ seed_insecure_rand();
+ doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
+
+ g_signals.DetectedDoubleSpend.connect(boost::bind(RelayDoubleSpend, _1, _2, _3, doubleSpendFilter));
}
+
void RegisterWallet(CWalletInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
@@ -203,6 +227,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;
@@ -213,6 +241,8 @@ struct CNodeState {
CNodeState() {
nMisbehavior = 0;
fShouldBan = false;
+ pindexBestKnownBlock = NULL;
+ hashLastUnknownBlock = uint256(0);
nBlocksToDownload = 0;
nBlocksInFlight = 0;
nLastBlockReceive = 0;
@@ -274,7 +304,6 @@ void MarkBlockAsReceived(const uint256 &hash, NodeId nodeFrom = -1) {
state->nLastBlockReceive = GetTimeMicros();
mapBlocksInFlight.erase(itInFlight);
}
-
}
// Requires cs_main.
@@ -310,14 +339,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;
}
@@ -371,8 +434,11 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
break;
// Exponentially larger steps back, plus the genesis block.
int nHeight = std::max(pindex->nHeight - nStep, 0);
+ // Jump back quickly to the same height as the chain.
+ if (pindex->nHeight > nHeight)
+ pindex = pindex->GetAncestor(nHeight);
// In case pindex is not in this chain, iterate pindex->pprev to find blocks.
- while (pindex->nHeight > nHeight && !Contains(pindex))
+ while (!Contains(pindex))
pindex = pindex->pprev;
// If pindex is in this chain, use direct height-based access.
if (pindex->nHeight > nHeight)
@@ -399,6 +465,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;
@@ -488,7 +556,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
@@ -521,7 +589,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
@@ -583,15 +651,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)
{
@@ -615,8 +681,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;
@@ -628,16 +695,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)
@@ -821,6 +892,21 @@ 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;
+}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee)
@@ -855,9 +941,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
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
+ g_signals.DetectedDoubleSpend(outpoint, tx, false);
return false;
}
}
@@ -929,23 +1016,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < CTransaction::minRelayTxFee.GetFee(nSize))
{
- static CCriticalSection csFreeLimiter;
static double dFreeCount;
- static int64_t nLastTime;
- int64_t nNow = GetTime();
+ static int64_t nLastFreeTime;
+ static int64_t nFreeLimit = GetArg("-limitfreerelay", 15);
- LOCK(csFreeLimiter);
-
- // 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)
@@ -968,6 +1047,48 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true;
}
+static void RelayDoubleSpend(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;
+
+ // 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;
+ }
+
+ 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);
+
+ RelayTransaction(doubleSpend);
+
+ // Share conflict with wallet
+ g_signals.SyncTransaction(doubleSpend, NULL);
+}
+
int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const
{
@@ -2105,6 +2226,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);
@@ -2462,6 +2584,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);
@@ -2812,6 +2983,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
@@ -3607,6 +3780,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);
}
@@ -3649,7 +3825,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\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit);
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -4038,6 +4214,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);
}
@@ -4352,6 +4529,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->fDisconnect = true;
}
+ // Update knowledge of peer's block availability.
+ ProcessBlockAvailability(pto->GetId());
+
//
// Message: getdata (blocks)
//
diff --git a/src/main.h b/src/main.h
index 9858bcfd69..19f4469008 100644
--- a/src/main.h
+++ b/src/main.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 */
@@ -106,6 +108,9 @@ struct CNodeStateStats;
struct CBlockTemplate;
+/** Set up internal signal handlers **/
+void RegisterInternalSignals();
+
/** Register a wallet to receive updates from core */
void RegisterWallet(CWalletInterface* pwalletIn);
/** Unregister a wallet from core */
@@ -183,6 +188,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
struct CNodeStateStats {
int nMisbehavior;
+ int nSyncHeight;
};
struct CDiskBlockPos
@@ -674,6 +680,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;
@@ -713,6 +722,7 @@ public:
{
phashBlock = NULL;
pprev = NULL;
+ pskip = NULL;
nHeight = 0;
nFile = 0;
nDataPos = 0;
@@ -734,6 +744,7 @@ public:
{
phashBlock = NULL;
pprev = NULL;
+ pskip = NULL;
nHeight = 0;
nFile = 0;
nDataPos = 0;
@@ -866,9 +877,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
diff --git a/src/net.h b/src/net.h
index 41dc618571..2ee798d468 100644
--- a/src/net.h
+++ b/src/net.h
@@ -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;
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..81b9054252 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -91,7 +91,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));
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/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 eec2b57e8c..21f1b7356f 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -170,6 +170,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)
@@ -213,6 +215,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)
{
@@ -221,6 +224,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)
{
@@ -231,13 +235,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..37679cebfa 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
@@ -133,7 +149,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..d7f4c043cf 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -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;
}
}
}
@@ -363,6 +363,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();
}
@@ -535,7 +537,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)
{
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 2f633a26c8..defc815def 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -138,6 +138,14 @@ void WalletModel::checkBalanceChanged()
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);
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 580c6bd5ba..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()));
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 98caf704ee..db60ef3592 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -443,7 +443,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
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("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 a300de2680..5b470516a1 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -22,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)
{
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index a54872ccc4..2d7abb2d58 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -134,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));
diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp
index 2718f81783..dd8692e802 100644
--- a/src/rpcprotocol.cpp
+++ b/src/rpcprotocol.cpp
@@ -54,7 +54,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 +85,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,
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/rpcserver.cpp b/src/rpcserver.cpp
index 6552de8c49..f47b3385da 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;
@@ -393,16 +383,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 +481,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 {
@@ -819,6 +799,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;
@@ -835,67 +880,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 1966a65b50..01e77163c4 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);
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 4f27cef087..38e96133b4 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -58,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)
@@ -1211,6 +1215,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"
@@ -1376,6 +1386,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"
@@ -1448,6 +1464,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"
diff --git a/src/script.cpp b/src/script.cpp
index bc4705abe7..e1b6985408 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -974,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
*/
@@ -1066,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)
{
@@ -1092,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)
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index b56d998be2..5c6c7d8588 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -45,6 +45,10 @@ 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)
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index a75593a8b2..6ae2b9cf64 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -256,46 +256,61 @@ 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());
CMutableTransaction txFrom;
- txFrom.vout.resize(6);
+ 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 (int 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));
@@ -303,19 +318,24 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
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;
}
- CMutableTransaction 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/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
new file mode 100644
index 0000000000..ea301685c9
--- /dev/null
+++ b/src/test/skiplist_tests.cpp
@@ -0,0 +1,45 @@
+// 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_SUITE_END()
+
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 52f82ef040..97a426dd35 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -415,7 +415,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);
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 9e4b2b787e..5a8f85ade7 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -77,11 +77,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;
@@ -419,6 +420,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)
diff --git a/src/util.h b/src/util.h
index 6057c72e66..707b8f2d76 100644
--- a/src/util.h
+++ b/src/util.h
@@ -156,6 +156,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);
diff --git a/src/wallet.cpp b/src/wallet.cpp
index f6fd6e958d..daca7ac04b 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -256,7 +256,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);
@@ -274,7 +274,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;
}
@@ -303,6 +304,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
@@ -588,6 +590,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", "");
@@ -610,7 +634,12 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
AssertLockHeld(cs_wallet);
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
@@ -896,7 +925,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);
@@ -916,13 +945,13 @@ void CWalletTx::RelayWalletTransaction()
}
}
-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;
diff --git a/src/wallet.h b/src/wallet.h
index 8494ce9a34..1c2512d678 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -141,6 +141,9 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
+ // Increment to cause UI refresh, similar to new block
+ int64_t nConflictsReceived;
+
CWallet()
{
SetNull();
@@ -163,6 +166,7 @@ public:
nNextResend = 0;
nLastResend = 0;
nTimeFirstKey = 0;
+ nConflictsReceived = 0;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -305,6 +309,13 @@ public:
{
return (GetDebit(tx) > 0);
}
+ 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
{
int64_t nDebit = 0;
@@ -377,7 +388,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.
@@ -699,7 +710,7 @@ public:
void RelayWalletTransaction();
- std::set<uint256> GetConflicts() const;
+ std::set<uint256> GetConflicts(bool includeEquivalent=true) const;
};