aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac14
-rw-r--r--contrib/README.md6
-rw-r--r--contrib/debian/examples/bitcoin.conf2
-rw-r--r--contrib/debian/manpages/bitcoin-qt.18
-rw-r--r--contrib/debian/manpages/bitcoin.conf.52
-rw-r--r--contrib/debian/manpages/bitcoind.12
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/pyminer/README.md8
-rw-r--r--contrib/pyminer/example-config.cfg32
-rwxr-xr-xcontrib/pyminer/pyminer.py252
-rw-r--r--doc/README.md2
-rw-r--r--doc/README_windows.txt2
-rw-r--r--doc/build-msw.md83
-rw-r--r--doc/release-notes.md46
-rw-r--r--doc/tor.md5
-rwxr-xr-xqa/pull-tester/run-bitcoind-for-test.sh.in2
-rwxr-xr-xqa/rpc-tests/getblocktemplate.py94
-rw-r--r--qa/rpc-tests/util.py8
-rw-r--r--src/Makefile.am8
-rw-r--r--src/Makefile.qt.include1
-rw-r--r--src/addrman.h265
-rw-r--r--src/allocators.h2
-rw-r--r--src/bloom.cpp7
-rw-r--r--src/bloom.h2
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/chainparamsbase.cpp4
-rw-r--r--src/chainparamsbase.h6
-rw-r--r--src/checkpoints.cpp5
-rw-r--r--src/coins.cpp15
-rw-r--r--src/coins.h15
-rw-r--r--src/compat.h15
-rw-r--r--src/core.cpp16
-rw-r--r--src/core.h3
-rw-r--r--src/init.cpp79
-rw-r--r--src/key.cpp10
-rw-r--r--src/leveldbwrapper.cpp5
-rw-r--r--src/m4/bitcoin_qt.m4110
-rw-r--r--src/main.cpp144
-rw-r--r--src/main.h6
-rw-r--r--src/net.cpp118
-rw-r--r--src/net.h19
-rw-r--r--src/netbase.cpp119
-rw-r--r--src/netbase.h4
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/bitcoin.cpp41
-rw-r--r--src/qt/bitcoinamountfield.cpp257
-rw-r--r--src/qt/bitcoinamountfield.h14
-rw-r--r--src/qt/bitcoingui.cpp25
-rw-r--r--src/qt/bitcoinunits.cpp86
-rw-r--r--src/qt/bitcoinunits.h60
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/guiconstants.h4
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/overviewpage.cpp18
-rw-r--r--src/qt/recentrequeststablemodel.cpp7
-rw-r--r--src/qt/rpcconsole.cpp29
-rw-r--r--src/qt/rpcconsole.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp8
-rw-r--r--src/qt/sendcoinsentry.cpp18
-rw-r--r--src/qt/transactiondesc.cpp28
-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.cpp28
-rw-r--r--src/qt/transactiontablemodel.h4
-rw-r--r--src/qt/transactionview.cpp21
-rw-r--r--src/qt/transactionview.h3
-rw-r--r--src/qt/walletmodel.cpp21
-rw-r--r--src/qt/walletmodel.h4
-rw-r--r--src/qt/walletview.cpp2
-rw-r--r--src/random.cpp139
-rw-r--r--src/random.h49
-rw-r--r--src/rpcclient.cpp2
-rw-r--r--src/rpcdump.cpp26
-rw-r--r--src/rpcmining.cpp83
-rw-r--r--src/rpcmisc.cpp13
-rw-r--r--src/rpcnet.cpp6
-rw-r--r--src/rpcrawtransaction.cpp2
-rw-r--r--src/rpcserver.cpp13
-rw-r--r--src/rpcserver.h2
-rw-r--r--src/rpcwallet.cpp79
-rw-r--r--src/script.cpp8
-rw-r--r--src/serialize.h29
-rw-r--r--src/sync.h3
-rw-r--r--src/test/bloom_tests.cpp4
-rw-r--r--src/test/canonical_tests.cpp8
-rw-r--r--src/test/crypto_tests.cpp1
-rw-r--r--src/test/mruset_tests.cpp1
-rw-r--r--src/test/sighash_tests.cpp16
-rw-r--r--src/test/skiplist_tests.cpp7
-rw-r--r--src/test/test_bitcoin.cpp4
-rw-r--r--src/test/util_tests.cpp1
-rw-r--r--src/timedata.cpp18
-rw-r--r--src/txmempool.cpp8
-rw-r--r--src/ui_interface.h5
-rw-r--r--src/uint256.cpp43
-rw-r--r--src/uint256.h4
-rw-r--r--src/util.cpp108
-rw-r--r--src/util.h50
-rw-r--r--src/version.h4
-rw-r--r--src/wallet.cpp90
-rw-r--r--src/wallet.h25
104 files changed, 1638 insertions, 1491 deletions
diff --git a/configure.ac b/configure.ac
index d3500b4d93..719b06da67 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,6 +258,9 @@ case $host in
CPPFLAGS="$CPPFLAGS -DMAC_OSX"
;;
+ *linux*)
+ TARGET_OS=linux
+ ;;
*)
;;
esac
@@ -389,6 +392,8 @@ AC_TRY_COMPILE([#include <sys/socket.h>],
[ AC_MSG_RESULT(no)]
)
+AC_SEARCH_LIBS([clock_gettime],[rt])
+
LEVELDB_CPPFLAGS=
LIBLEVELDB=
LIBMEMENV=
@@ -457,11 +462,8 @@ dnl after 1.56.
dnl If neither is available, abort.
dnl If sleep_for is used, boost_chrono becomes a requirement.
if test x$ax_cv_boost_chrono = xyes; then
-dnl Allow passing extra needed dependency libraries for boost-chrono from static gitian build
-BOOST_CHRONO_LIB="$BOOST_CHRONO_LIB $BOOST_CHRONO_EXTRALIBS"
-
TEMP_LIBS="$LIBS"
-LIBS="$LIBS $BOOST_LIBS $BOOST_CHRONO_LIB"
+LIBS="$BOOST_LIBS $BOOST_CHRONO_LIB $LIBS"
TEMP_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
AC_TRY_LINK([
@@ -483,7 +485,7 @@ fi
if test x$boost_sleep != xyes; then
TEMP_LIBS="$LIBS"
-LIBS="$LIBS $BOOST_LIBS"
+LIBS="$BOOST_LIBS $LIBS"
TEMP_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
AC_TRY_LINK([
@@ -523,7 +525,7 @@ BITCOIN_QT_INIT
if test x$use_pkgconfig = xyes; then
- if test x$PKG_CONFIG == x; then
+ if test x"$PKG_CONFIG" == "x"; then
AC_MSG_ERROR(pkg-config not found.)
fi
diff --git a/contrib/README.md b/contrib/README.md
index 92d0a343db..dae975e9ef 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -19,13 +19,9 @@ Contains the script `github-merge.sh` for merging github pull requests securely
### [Linearize](/contrib/linearize) ###
Construct a linear, no-fork, best version of the blockchain.
-### [PyMiner](/contrib/pyminer) ###
-
-This is a 'getwork' CPU mining client for Bitcoin. It is pure-python, and therefore very, very slow. The purpose is to provide a reference implementation of a miner, for study.
-
### [Qos](/contrib/qos) ###
-A Linux bash script that will set up tc to limit the outgoing bandwidth for connections to the Bitcoin network. This means one can have an always-on bitcoind instance running, and another local bitcoind/bitcoin-qt instance which connects to this node and receives blocks from it.
+A Linux bash script that will set up traffic control (tc) to limit the outgoing bandwidth for connections to the Bitcoin network. This means one can have an always-on bitcoind instance running, and another local bitcoind/bitcoin-qt instance which connects to this node and receives blocks from it.
### [Seeds](/contrib/seeds) ###
Utility to generate the pnSeed[] array that is compiled into the client.
diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf
index 0aa8674af9..31cca981e0 100644
--- a/contrib/debian/examples/bitcoin.conf
+++ b/contrib/debian/examples/bitcoin.conf
@@ -10,7 +10,7 @@
# Run a regression test network
#regtest=0
-# Connect via a socks4 proxy
+# Connect via a SOCKS5 proxy
#proxy=127.0.0.1:9050
##############################################################
diff --git a/contrib/debian/manpages/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1
index 25a423f9c4..a023582bc0 100644
--- a/contrib/debian/manpages/bitcoin-qt.1
+++ b/contrib/debian/manpages/bitcoin-qt.1
@@ -32,10 +32,7 @@ Set database cache size in megabytes (default: 25)
Specify connection timeout in milliseconds (default: 5000)
.TP
\fB\-proxy=\fR<ip:port>
-Connect through socks proxy
-.TP
-\fB\-socks=\fR<n>
-Select the version of socks proxy to use (4\-5, default: 5)
+Connect through SOCKS5 proxy
.TP
\fB\-tor=\fR<ip:port>
Use proxy to reach tor hidden services (default: same as \fB\-proxy\fR)
@@ -139,9 +136,6 @@ 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/debian/manpages/bitcoin.conf.5 b/contrib/debian/manpages/bitcoin.conf.5
index 7438b4b66a..8a0078d5d5 100644
--- a/contrib/debian/manpages/bitcoin.conf.5
+++ b/contrib/debian/manpages/bitcoin.conf.5
@@ -2,7 +2,7 @@
.SH NAME
bitcoin.conf \- bitcoin configuration file
.SH SYNOPSIS
-All command-line options (except for '\-datadir' and '\-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file.
+All command-line options (except for '\-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file.
.TP
The configuration file is a list of 'setting=value' pairs, one per line, with optional comments starting with the '#' character.
.TP
diff --git a/contrib/debian/manpages/bitcoind.1 b/contrib/debian/manpages/bitcoind.1
index 0c191b6043..a1b17d6077 100644
--- a/contrib/debian/manpages/bitcoind.1
+++ b/contrib/debian/manpages/bitcoind.1
@@ -28,7 +28,7 @@ Start minimized
Specify data directory
.TP
\fB\-proxy=\fR<ip:port>
-Connect through socks4 proxy
+Connect through SOCKS5 proxy
.TP
\fB\-addnode=\fR<ip>
Add a node to connect to
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index 65a6c3c1e9..30b0227bdd 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -59,7 +59,7 @@ script: |
local: *;
};' > $LINKER_SCRIPT
function do_configure {
- ./configure "$@" --enable-upnp-default --prefix=$STAGING --with-protoc-bindir=$STAGING/host/bin --with-qt-bindir=$STAGING/bin --with-boost=$STAGING --disable-maintainer-mode --disable-dependency-tracking PKG_CONFIG_PATH="$STAGING/lib/pkgconfig" CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib -Wl,--version-script=$LINKER_SCRIPT ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}" BOOST_CHRONO_EXTRALIBS="-lrt" --enable-glibc-back-compat
+ ./configure "$@" --enable-upnp-default --prefix=$STAGING --with-protoc-bindir=$STAGING/host/bin --with-qt-bindir=$STAGING/bin --with-boost=$STAGING --disable-maintainer-mode --disable-dependency-tracking PKG_CONFIG_PATH="$STAGING/lib/pkgconfig" CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib -Wl,--version-script=$LINKER_SCRIPT ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}" --enable-glibc-back-compat
}
#
cd bitcoin
diff --git a/contrib/pyminer/README.md b/contrib/pyminer/README.md
deleted file mode 100644
index 3b20f2fdea..0000000000
--- a/contrib/pyminer/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-### PyMiner ###
-
-This is a 'getwork' CPU mining client for Bitcoin. It is pure-python, and therefore very, very slow. The purpose is to provide a reference implementation of a miner, for study.
-
-### Other Resources ###
-
-- [BitcoinTalk Thread](https://bitcointalk.org/index.php?topic=3546.0)
-- [Jgarzik Repo](https://github.com/jgarzik/pyminer) \ No newline at end of file
diff --git a/contrib/pyminer/example-config.cfg b/contrib/pyminer/example-config.cfg
deleted file mode 100644
index 103e7c1372..0000000000
--- a/contrib/pyminer/example-config.cfg
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#
-# RPC login details
-#
-host=127.0.0.1
-port=8332
-
-rpcuser=myusername
-rpcpass=mypass
-
-
-#
-# mining details
-#
-
-threads=4
-
-# periodic rate for requesting new work, if solution not found
-scantime=60
-
-
-#
-# misc.
-#
-
-# not really used right now
-logdir=/tmp/pyminer
-
-# set to 1, to enable hashmeter output
-hashmeter=0
-
-
diff --git a/contrib/pyminer/pyminer.py b/contrib/pyminer/pyminer.py
deleted file mode 100755
index 0a2932d66e..0000000000
--- a/contrib/pyminer/pyminer.py
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (c) 2011 The Bitcoin developers
-# Distributed under the MIT/X11 software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-
-import time
-import json
-import pprint
-import hashlib
-import struct
-import re
-import base64
-import httplib
-import sys
-from multiprocessing import Process
-
-ERR_SLEEP = 15
-MAX_NONCE = 1000000L
-
-settings = {}
-pp = pprint.PrettyPrinter(indent=4)
-
-class BitcoinRPC:
- OBJID = 1
-
- def __init__(self, host, port, username, password):
- authpair = "%s:%s" % (username, password)
- self.authhdr = "Basic %s" % (base64.b64encode(authpair))
- self.conn = httplib.HTTPConnection(host, port, False, 30)
- def rpc(self, method, params=None):
- self.OBJID += 1
- obj = { 'version' : '1.1',
- 'method' : method,
- 'id' : self.OBJID }
- if params is None:
- obj['params'] = []
- else:
- obj['params'] = params
- self.conn.request('POST', '/', json.dumps(obj),
- { 'Authorization' : self.authhdr,
- 'Content-type' : 'application/json' })
-
- resp = self.conn.getresponse()
- if resp is None:
- print "JSON-RPC: no response"
- return None
-
- body = resp.read()
- resp_obj = json.loads(body)
- if resp_obj is None:
- print "JSON-RPC: cannot JSON-decode body"
- return None
- if 'error' in resp_obj and resp_obj['error'] != None:
- return resp_obj['error']
- if 'result' not in resp_obj:
- print "JSON-RPC: no result in object"
- return None
-
- return resp_obj['result']
- def getblockcount(self):
- return self.rpc('getblockcount')
- def getwork(self, data=None):
- return self.rpc('getwork', data)
-
-def uint32(x):
- return x & 0xffffffffL
-
-def bytereverse(x):
- return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
- (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
-
-def bufreverse(in_buf):
- out_words = []
- for i in range(0, len(in_buf), 4):
- word = struct.unpack('@I', in_buf[i:i+4])[0]
- out_words.append(struct.pack('@I', bytereverse(word)))
- return ''.join(out_words)
-
-def wordreverse(in_buf):
- out_words = []
- for i in range(0, len(in_buf), 4):
- out_words.append(in_buf[i:i+4])
- out_words.reverse()
- return ''.join(out_words)
-
-class Miner:
- def __init__(self, id):
- self.id = id
- self.max_nonce = MAX_NONCE
-
- def work(self, datastr, targetstr):
- # decode work data hex string to binary
- static_data = datastr.decode('hex')
- static_data = bufreverse(static_data)
-
- # the first 76b of 80b do not change
- blk_hdr = static_data[:76]
-
- # decode 256-bit target value
- targetbin = targetstr.decode('hex')
- targetbin = targetbin[::-1] # byte-swap and dword-swap
- targetbin_str = targetbin.encode('hex')
- target = long(targetbin_str, 16)
-
- # pre-hash first 76b of block header
- static_hash = hashlib.sha256()
- static_hash.update(blk_hdr)
-
- for nonce in xrange(self.max_nonce):
-
- # encode 32-bit nonce value
- nonce_bin = struct.pack("<I", nonce)
-
- # hash final 4b, the nonce value
- hash1_o = static_hash.copy()
- hash1_o.update(nonce_bin)
- hash1 = hash1_o.digest()
-
- # sha256 hash of sha256 hash
- hash_o = hashlib.sha256()
- hash_o.update(hash1)
- hash = hash_o.digest()
-
- # quick test for winning solution: high 32 bits zero?
- if hash[-4:] != '\0\0\0\0':
- continue
-
- # convert binary hash to 256-bit Python long
- hash = bufreverse(hash)
- hash = wordreverse(hash)
-
- hash_str = hash.encode('hex')
- l = long(hash_str, 16)
-
- # proof-of-work test: hash < target
- if l < target:
- print time.asctime(), "PROOF-OF-WORK found: %064x" % (l,)
- return (nonce + 1, nonce_bin)
- else:
- print time.asctime(), "PROOF-OF-WORK false positive %064x" % (l,)
-# return (nonce + 1, nonce_bin)
-
- return (nonce + 1, None)
-
- def submit_work(self, rpc, original_data, nonce_bin):
- nonce_bin = bufreverse(nonce_bin)
- nonce = nonce_bin.encode('hex')
- solution = original_data[:152] + nonce + original_data[160:256]
- param_arr = [ solution ]
- result = rpc.getwork(param_arr)
- print time.asctime(), "--> Upstream RPC result:", result
-
- def iterate(self, rpc):
- work = rpc.getwork()
- if work is None:
- time.sleep(ERR_SLEEP)
- return
- if 'data' not in work or 'target' not in work:
- time.sleep(ERR_SLEEP)
- return
-
- time_start = time.time()
-
- (hashes_done, nonce_bin) = self.work(work['data'],
- work['target'])
-
- time_end = time.time()
- time_diff = time_end - time_start
-
- self.max_nonce = long(
- (hashes_done * settings['scantime']) / time_diff)
- if self.max_nonce > 0xfffffffaL:
- self.max_nonce = 0xfffffffaL
-
- if settings['hashmeter']:
- print "HashMeter(%d): %d hashes, %.2f Khash/sec" % (
- self.id, hashes_done,
- (hashes_done / 1000.0) / time_diff)
-
- if nonce_bin is not None:
- self.submit_work(rpc, work['data'], nonce_bin)
-
- def loop(self):
- rpc = BitcoinRPC(settings['host'], settings['port'],
- settings['rpcuser'], settings['rpcpass'])
- if rpc is None:
- return
-
- while True:
- self.iterate(rpc)
-
-def miner_thread(id):
- miner = Miner(id)
- miner.loop()
-
-if __name__ == '__main__':
- if len(sys.argv) != 2:
- print "Usage: pyminer.py CONFIG-FILE"
- sys.exit(1)
-
- f = open(sys.argv[1])
- for line in f:
- # skip comment lines
- m = re.search('^\s*#', line)
- if m:
- continue
-
- # parse key=value lines
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
- if m is None:
- continue
- settings[m.group(1)] = m.group(2)
- f.close()
-
- if 'host' not in settings:
- settings['host'] = '127.0.0.1'
- if 'port' not in settings:
- settings['port'] = 8332
- if 'threads' not in settings:
- settings['threads'] = 1
- if 'hashmeter' not in settings:
- settings['hashmeter'] = 0
- if 'scantime' not in settings:
- settings['scantime'] = 30L
- if 'rpcuser' not in settings or 'rpcpass' not in settings:
- print "Missing username and/or password in cfg file"
- sys.exit(1)
-
- settings['port'] = int(settings['port'])
- settings['threads'] = int(settings['threads'])
- settings['hashmeter'] = int(settings['hashmeter'])
- settings['scantime'] = long(settings['scantime'])
-
- thr_list = []
- for thr_id in range(settings['threads']):
- p = Process(target=miner_thread, args=(thr_id,))
- p.start()
- thr_list.append(p)
- time.sleep(1) # stagger threads
-
- print settings['threads'], "mining threads started"
-
- print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port'])
- try:
- for thr_proc in thr_list:
- thr_proc.join()
- except KeyboardInterrupt:
- pass
- print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port'])
-
diff --git a/doc/README.md b/doc/README.md
index 9c724ec1e9..f8bb8020d4 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -73,5 +73,5 @@ The Bitcoin repo's [root README](https://github.com/bitcoin/bitcoin/blob/master/
License
---------------------
Distributed under the [MIT/X11 software license](http://www.opensource.org/licenses/mit-license.php).
-This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](http://www.openssl.org/). This product includes
+This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](https://www.openssl.org/). This product includes
cryptographic software written by Eric Young ([eay@cryptsoft.com](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard.
diff --git a/doc/README_windows.txt b/doc/README_windows.txt
index 18fd4216f9..368f2b45e1 100644
--- a/doc/README_windows.txt
+++ b/doc/README_windows.txt
@@ -5,7 +5,7 @@ Copyright (c) 2009-2014 Bitcoin Core Developers
Distributed under the MIT/X11 software license, see the accompanying
file COPYING or http://www.opensource.org/licenses/mit-license.php.
This product includes software developed by the OpenSSL Project for use in
-the OpenSSL Toolkit (http://www.openssl.org/). This product includes
+the OpenSSL Toolkit (https://www.openssl.org/). This product includes
cryptographic software written by Eric Young (eay@cryptsoft.com).
diff --git a/doc/build-msw.md b/doc/build-msw.md
deleted file mode 100644
index 9e4eaee3f5..0000000000
--- a/doc/build-msw.md
+++ /dev/null
@@ -1,83 +0,0 @@
-WINDOWS BUILD NOTES
-===================
-
-
-Compilers Supported
--------------------
-TODO: What works?
-Note: releases are cross-compiled using mingw running on Linux.
-
-
-Dependencies
-------------
-Libraries you need to download separately and build:
-
- name default path download
- --------------------------------------------------------------------------------------------------------------------
- OpenSSL \openssl-1.0.1c-mgw http://www.openssl.org/source/
- Berkeley DB \db-4.8.30.NC-mgw http://www.oracle.com/technology/software/products/berkeley-db/index.html
- Boost \boost-1.50.0-mgw http://www.boost.org/users/download/
- miniupnpc \miniupnpc-1.6-mgw http://miniupnp.tuxfamily.org/files/
-
-Their licenses:
-
- OpenSSL Old BSD license with the problematic advertising requirement
- Berkeley DB New BSD license with additional requirement that linked software must be free open source
- Boost MIT-like license
- miniupnpc New (3-clause) BSD license
-
-Versions used in this release:
-
- OpenSSL 1.0.1c
- Berkeley DB 4.8.30.NC
- Boost 1.50.0
- miniupnpc 1.6
-
-
-OpenSSL
--------
-MSYS shell:
-
-un-tar sources with MSYS 'tar xfz' to avoid issue with symlinks (OpenSSL ticket 2377)
-change 'MAKE' env. variable from 'C:\MinGW32\bin\mingw32-make.exe' to '/c/MinGW32/bin/mingw32-make.exe'
-
- cd /c/openssl-1.0.1c-mgw
- ./config
- make
-
-Berkeley DB
------------
-MSYS shell:
-
- cd /c/db-4.8.30.NC-mgw/build_unix
- sh ../dist/configure --enable-mingw --enable-cxx
- make
-
-Boost
------
-MSYS shell:
-
- downloaded boost jam 3.1.18
- cd \boost-1.50.0-mgw
- bjam toolset=gcc --build-type=complete stage
-
-MiniUPnPc
----------
-UPnP support is optional, make with `USE_UPNP=` to disable it.
-
-MSYS shell:
-
- cd /c/miniupnpc-1.6-mgw
- make -f Makefile.mingw
- mkdir miniupnpc
- cp *.h miniupnpc/
-
-Bitcoin
--------
-MSYS shell:
-
- cd \bitcoin
- sh autogen.sh
- sh configure
- mingw32-make
- strip bitcoind.exe
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 66059800b6..967a39a0e7 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -39,49 +39,3 @@ 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/tor.md b/doc/tor.md
index b5eb91e12e..560f71fa27 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -13,11 +13,6 @@ configure Tor.
The first step is running Bitcoin behind a Tor proxy. This will already make all
outgoing connections be anonymized, but more is possible.
- -socks=5 SOCKS5 supports connecting-to-hostname, which can be used instead
- of doing a (leaking) local DNS lookup. SOCKS5 is the default,
- but SOCKS4 does not support this. (SOCKS4a does, but isn't
- implemented).
-
-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
server will be used to try to reach .onion addresses as well.
diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in
index 391046ab85..ecc42e12b1 100755
--- a/qa/pull-tester/run-bitcoind-for-test.sh.in
+++ b/qa/pull-tester/run-bitcoind-for-test.sh.in
@@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
WAITER=$!
PORT=`expr $BASHPID + 10000`
-"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -regtest -rpcport=`expr $PORT + 1` &
+"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
BITCOIND=$!
#Install a watchdog.
diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py
new file mode 100755
index 0000000000..8d97719ec3
--- /dev/null
+++ b/qa/rpc-tests/getblocktemplate.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The Bitcoin Core developers
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+# Exercise the listtransactions API
+
+from test_framework import BitcoinTestFramework
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+
+def check_array_result(object_array, to_match, expected):
+ """
+ Pass in array of JSON objects, a dictionary with key/value pairs
+ to match against, and another dictionary with expected key/value
+ pairs.
+ """
+ num_matched = 0
+ for item in object_array:
+ all_match = True
+ for key,value in to_match.items():
+ if item[key] != value:
+ all_match = False
+ if not all_match:
+ continue
+ for key,value in expected.items():
+ if item[key] != value:
+ raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
+ num_matched = num_matched+1
+ if num_matched == 0:
+ raise AssertionError("No objects matched %s"%(str(to_match)))
+
+import threading
+
+class LongpollThread(threading.Thread):
+ def __init__(self, node):
+ threading.Thread.__init__(self)
+ # query current longpollid
+ templat = node.getblocktemplate()
+ self.longpollid = templat['longpollid']
+ # create a new connection to the node, we can't use the same
+ # connection from two threads
+ self.node = AuthServiceProxy(node.url, timeout=600)
+
+ def run(self):
+ self.node.getblocktemplate({'longpollid':self.longpollid})
+
+class GetBlockTemplateTest(BitcoinTestFramework):
+ '''
+ Test longpolling with getblocktemplate.
+ '''
+
+ def run_test(self, nodes):
+ print "Warning: this test will take about 70 seconds in the best case. Be patient."
+ nodes[0].setgenerate(True, 10)
+ templat = nodes[0].getblocktemplate()
+ longpollid = templat['longpollid']
+ # longpollid should not change between successive invocations if nothing else happens
+ templat2 = nodes[0].getblocktemplate()
+ assert(templat2['longpollid'] == longpollid)
+
+ # Test 1: test that the longpolling wait if we do nothing
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ # check that thread still lives
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(thr.is_alive())
+
+ # Test 2: test that longpoll will terminate if another node generates a block
+ nodes[1].setgenerate(True, 1) # generate a block on another node
+ # check that thread will exit now that new transaction entered mempool
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(not thr.is_alive())
+
+ # Test 3: test that longpoll will terminate if we generate a block ourselves
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ nodes[0].setgenerate(True, 1) # generate a block on another node
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(not thr.is_alive())
+
+ # Test 4: test that introducing a new transaction into the mempool will terminate the longpoll
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ # generate a random transaction and submit it
+ (txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"), Decimal("0.0"), Decimal("0.001"), 20)
+ # after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned
+ thr.join(60 + 20)
+ assert(not thr.is_alive())
+
+if __name__ == '__main__':
+ GetBlockTemplateTest().main()
+
diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py
index 0a7f26ffcd..da2b6df197 100644
--- a/qa/rpc-tests/util.py
+++ b/qa/rpc-tests/util.py
@@ -85,7 +85,7 @@ def initialize_chain(test_dir):
# Create cache directories, run bitcoinds:
for i in range(4):
datadir=initialize_datadir("cache", i)
- args = [ "bitcoind", "-keypool=1", "-datadir="+datadir ]
+ args = [ "bitcoind", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
if i > 0:
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
bitcoind_processes[i] = subprocess.Popen(args)
@@ -147,7 +147,7 @@ def start_node(i, dir, extra_args=None, rpchost=None):
Start a bitcoind and return RPC connection to it
"""
datadir = os.path.join(dir, "node"+str(i))
- args = [ "bitcoind", "-datadir="+datadir, "-keypool=1" ]
+ args = [ "bitcoind", "-datadir="+datadir, "-keypool=1", "-discover=0" ]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args)
devnull = open("/dev/null", "w+")
@@ -156,7 +156,9 @@ def start_node(i, dir, extra_args=None, rpchost=None):
["-rpcwait", "getblockcount"], stdout=devnull)
devnull.close()
url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
- return AuthServiceProxy(url)
+ proxy = AuthServiceProxy(url)
+ proxy.url = url # store URL on proxy for info
+ return proxy
def start_nodes(num_nodes, dir, extra_args=None, rpchost=None):
"""
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b0b97b7ac..ff23747592 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,6 +91,7 @@ BITCOIN_CORE_H = \
noui.h \
pow.h \
protocol.h \
+ random.h \
rpcclient.h \
rpcprotocol.h \
rpcserver.h \
@@ -197,14 +198,15 @@ libbitcoin_common_a_SOURCES = \
# backward-compatibility objects and their sanity checks are linked.
libbitcoin_util_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_util_a_SOURCES = \
+ compat/glibc_sanity.cpp \
+ compat/glibcxx_sanity.cpp \
chainparamsbase.cpp \
+ random.cpp \
rpcprotocol.cpp \
sync.cpp \
uint256.cpp \
util.cpp \
version.cpp \
- compat/glibc_sanity.cpp \
- compat/glibcxx_sanity.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
@@ -274,7 +276,7 @@ EXTRA_DIST = leveldb secp256k1
clean-local:
-$(MAKE) -C leveldb clean
- -$(MAKE) -C secp256k1 clean
+ -$(MAKE) -C secp256k1 clean 2>/dev/null
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
-rm -f config.h
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 75b7b683dd..2772bc753a 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -145,6 +145,7 @@ BITCOIN_MM = \
QT_MOC = \
qt/bitcoin.moc \
+ qt/bitcoinamountfield.moc \
qt/intro.moc \
qt/overviewpage.moc \
qt/rpcconsole.moc
diff --git a/src/addrman.h b/src/addrman.h
index c4c296560e..052d364655 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -7,6 +7,7 @@
#include "netbase.h"
#include "protocol.h"
+#include "random.h"
#include "sync.h"
#include "timedata.h"
#include "util.h"
@@ -16,8 +17,6 @@
#include <stdint.h>
#include <vector>
-#include <openssl/rand.h>
-
/** Extended statistics about a CAddress */
class CAddrInfo : public CAddress
{
@@ -246,145 +245,147 @@ protected:
void Connected_(const CService &addr, int64_t nTime);
public:
+ // serialized format:
+ // * version byte (currently 0)
+ // * nKey
+ // * nNew
+ // * nTried
+ // * number of "new" buckets
+ // * all nNew addrinfos in vvNew
+ // * all nTried addrinfos in vvTried
+ // * for each bucket:
+ // * number of elements
+ // * for each element: index
+ //
+ // Notice that vvTried, mapAddr and vVector are never encoded explicitly;
+ // they are instead reconstructed from the other information.
+ //
+ // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
+ // otherwise it is reconstructed as well.
+ //
+ // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
+ // changes to the ADDRMAN_ parameters without breaking the on-disk structure.
+ //
+ // We don't use IMPLEMENT_SERIALIZE since the serialization and deserialization code has
+ // very little in common.
+ template<typename Stream>
+ void Serialize(Stream &s, int nType, int nVersionDummy) const
+ {
+ LOCK(cs);
+
+ unsigned char nVersion = 0;
+ s << nVersion;
+ s << nKey;
+ s << nNew;
+ s << nTried;
+
+ int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
+ s << nUBuckets;
+ std::map<int, int> mapUnkIds;
+ int nIds = 0;
+ for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
+ if (nIds == nNew) break; // this means nNew was wrong, oh ow
+ mapUnkIds[(*it).first] = nIds;
+ const CAddrInfo &info = (*it).second;
+ if (info.nRefCount) {
+ s << info;
+ nIds++;
+ }
+ }
+ nIds = 0;
+ for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
+ if (nIds == nTried) break; // this means nTried was wrong, oh ow
+ const CAddrInfo &info = (*it).second;
+ if (info.fInTried) {
+ s << info;
+ nIds++;
+ }
+ }
+ for (std::vector<std::set<int> >::const_iterator it = vvNew.begin(); it != vvNew.end(); it++) {
+ const std::set<int> &vNew = (*it);
+ int nSize = vNew.size();
+ s << nSize;
+ for (std::set<int>::const_iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) {
+ int nIndex = mapUnkIds[*it2];
+ s << nIndex;
+ }
+ }
+ }
- IMPLEMENT_SERIALIZE
- (({
- // serialized format:
- // * version byte (currently 0)
- // * nKey
- // * nNew
- // * nTried
- // * number of "new" buckets
- // * all nNew addrinfos in vvNew
- // * all nTried addrinfos in vvTried
- // * for each bucket:
- // * number of elements
- // * for each element: index
- //
- // Notice that vvTried, mapAddr and vVector are never encoded explicitly;
- // they are instead reconstructed from the other information.
- //
- // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
- // otherwise it is reconstructed as well.
- //
- // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
- // changes to the ADDRMAN_ parameters without breaking the on-disk structure.
- {
- LOCK(cs);
- unsigned char nVersion = 0;
- READWRITE(nVersion);
- READWRITE(nKey);
- READWRITE(nNew);
- READWRITE(nTried);
-
- CAddrMan *am = const_cast<CAddrMan*>(this);
- if (fWrite)
- {
- int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
- READWRITE(nUBuckets);
- std::map<int, int> mapUnkIds;
- int nIds = 0;
- for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
- {
- if (nIds == nNew) break; // this means nNew was wrong, oh ow
- mapUnkIds[(*it).first] = nIds;
- CAddrInfo &info = (*it).second;
- if (info.nRefCount)
- {
- READWRITE(info);
- nIds++;
- }
- }
- nIds = 0;
- for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
- {
- if (nIds == nTried) break; // this means nTried was wrong, oh ow
- CAddrInfo &info = (*it).second;
- if (info.fInTried)
- {
- READWRITE(info);
- nIds++;
- }
- }
- for (std::vector<std::set<int> >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++)
- {
- const std::set<int> &vNew = (*it);
- int nSize = vNew.size();
- READWRITE(nSize);
- for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++)
- {
- int nIndex = mapUnkIds[*it2];
- READWRITE(nIndex);
- }
- }
+ template<typename Stream>
+ void Unserialize(Stream& s, int nType, int nVersionDummy)
+ {
+ LOCK(cs);
+
+ unsigned char nVersion;
+ s >> nVersion;
+ s >> nKey;
+ s >> nNew;
+ s >> nTried;
+
+ int nUBuckets = 0;
+ s >> nUBuckets;
+ nIdCount = 0;
+ mapInfo.clear();
+ mapAddr.clear();
+ vRandom.clear();
+ vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
+ vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
+ for (int n = 0; n < nNew; n++) {
+ CAddrInfo &info = mapInfo[n];
+ s >> info;
+ mapAddr[info] = n;
+ info.nRandomPos = vRandom.size();
+ vRandom.push_back(n);
+ if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
+ vvNew[info.GetNewBucket(nKey)].insert(n);
+ info.nRefCount++;
+ }
+ }
+ nIdCount = nNew;
+ int nLost = 0;
+ for (int n = 0; n < nTried; n++) {
+ CAddrInfo info;
+ s >> info;
+ std::vector<int> &vTried = vvTried[info.GetTriedBucket(nKey)];
+ if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) {
+ info.nRandomPos = vRandom.size();
+ info.fInTried = true;
+ vRandom.push_back(nIdCount);
+ mapInfo[nIdCount] = info;
+ mapAddr[info] = nIdCount;
+ vTried.push_back(nIdCount);
+ nIdCount++;
} else {
- int nUBuckets = 0;
- READWRITE(nUBuckets);
- am->nIdCount = 0;
- am->mapInfo.clear();
- am->mapAddr.clear();
- am->vRandom.clear();
- am->vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
- am->vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
- for (int n = 0; n < am->nNew; n++)
- {
- CAddrInfo &info = am->mapInfo[n];
- READWRITE(info);
- am->mapAddr[info] = n;
- info.nRandomPos = vRandom.size();
- am->vRandom.push_back(n);
- if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
- {
- am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
- info.nRefCount++;
- }
- }
- am->nIdCount = am->nNew;
- int nLost = 0;
- for (int n = 0; n < am->nTried; n++)
- {
- CAddrInfo info;
- READWRITE(info);
- std::vector<int> &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
- if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
- {
- info.nRandomPos = vRandom.size();
- info.fInTried = true;
- am->vRandom.push_back(am->nIdCount);
- am->mapInfo[am->nIdCount] = info;
- am->mapAddr[info] = am->nIdCount;
- vTried.push_back(am->nIdCount);
- am->nIdCount++;
- } else {
- nLost++;
- }
- }
- am->nTried -= nLost;
- for (int b = 0; b < nUBuckets; b++)
- {
- std::set<int> &vNew = am->vvNew[b];
- int nSize = 0;
- READWRITE(nSize);
- for (int n = 0; n < nSize; n++)
- {
- int nIndex = 0;
- READWRITE(nIndex);
- CAddrInfo &info = am->mapInfo[nIndex];
- if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
- {
- info.nRefCount++;
- vNew.insert(nIndex);
- }
- }
+ nLost++;
+ }
+ }
+ nTried -= nLost;
+ for (int b = 0; b < nUBuckets; b++) {
+ std::set<int> &vNew = vvNew[b];
+ int nSize = 0;
+ s >> nSize;
+ for (int n = 0; n < nSize; n++) {
+ int nIndex = 0;
+ s >> nIndex;
+ CAddrInfo &info = mapInfo[nIndex];
+ if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
+ info.nRefCount++;
+ vNew.insert(nIndex);
}
}
}
- });)
+ }
+
+ unsigned int GetSerializeSize(int nType, int nVersion) const
+ {
+ return (CSizeComputer(nType, nVersion) << *this).size();
+ }
CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
{
nKey.resize(32);
- RAND_bytes(&nKey[0], 32);
+ GetRandBytes(&nKey[0], 32);
nIdCount = 0;
nTried = 0;
diff --git a/src/allocators.h b/src/allocators.h
index 7012ef7e2a..be0be7ab96 100644
--- a/src/allocators.h
+++ b/src/allocators.h
@@ -131,7 +131,7 @@ public:
* Due to the unpredictable order of static initializers, we have to make sure the
* LockedPageManager instance exists before any other STL-based objects that use
* secure_allocator are created. So instead of having LockedPageManager also be
- * static-intialized, it is created on demand.
+ * static-initialized, it is created on demand.
*/
class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker>
{
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 85a2ddc189..26e366179c 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -94,13 +94,6 @@ 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 d0caf9e9fa..956bead87f 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -78,8 +78,6 @@ 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/chainparams.cpp b/src/chainparams.cpp
index b9097ea961..fb1d05f833 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -6,6 +6,7 @@
#include "chainparams.h"
#include "assert.h"
+#include "random.h"
#include "util.h"
#include <boost/assign/list_of.hpp>
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 19a9e72cc9..720e24c4a8 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -91,3 +91,7 @@ bool SelectBaseParamsFromCommandLine() {
}
return true;
}
+
+bool AreBaseParamsConfigured() {
+ return pCurrentBaseParams != NULL;
+}
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 4a3b268909..4398f69548 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -49,4 +49,10 @@ void SelectBaseParams(CBaseChainParams::Network network);
*/
bool SelectBaseParamsFromCommandLine();
+/**
+ * Return true if SelectBaseParamsFromCommandLine() has been called to select
+ * a network.
+ */
+bool AreBaseParamsConfigured();
+
#endif
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 4cab11db3d..717f0b90fe 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -51,11 +51,12 @@ namespace Checkpoints {
(225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"))
(250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"))
(279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"))
+ (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"))
;
static const CCheckpointData data = {
&mapCheckpoints,
- 1389047471, // * UNIX timestamp of last checkpoint block
- 30549816, // * total number of transactions between genesis and last checkpoint
+ 1397080064, // * UNIX timestamp of last checkpoint block
+ 36544669, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
60000.0 // * estimated number of transactions per day after checkpoint
};
diff --git a/src/coins.cpp b/src/coins.cpp
index 13a4ea95cd..fe40911db7 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -4,6 +4,8 @@
#include "coins.h"
+#include "random.h"
+
#include <assert.h>
// calculate number of bytes for the bitmask, and its number of non-zero bytes
@@ -69,6 +71,8 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
+CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
+
CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { }
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) {
@@ -84,8 +88,8 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) {
}
CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) {
- CCoinsMap::iterator it = cacheCoins.lower_bound(txid);
- if (it != cacheCoins.end() && it->first == txid)
+ CCoinsMap::iterator it = cacheCoins.find(txid);
+ if (it != cacheCoins.end())
return it;
CCoins tmp;
if (!base->GetCoins(txid,tmp))
@@ -107,7 +111,12 @@ bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) {
}
bool CCoinsViewCache::HaveCoins(const uint256 &txid) {
- return FetchCoins(txid) != cacheCoins.end();
+ CCoinsMap::iterator it = FetchCoins(txid);
+ // We're using vtx.empty() instead of IsPruned here for performance reasons,
+ // as we only care about the case where an transaction was replaced entirely
+ // in a reorganization (which wipes vout entirely, as opposed to spending
+ // which just cleans individual outputs).
+ return (it != cacheCoins.end() && !it->second.vout.empty());
}
uint256 CCoinsViewCache::GetBestBlock() {
diff --git a/src/coins.h b/src/coins.h
index c57a5ec722..9f90fe6bd0 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <boost/foreach.hpp>
+#include <boost/unordered_map.hpp>
/** pruned version of CTransaction: only retains metadata and unspent transaction outputs
*
@@ -239,7 +240,19 @@ public:
}
};
-typedef std::map<uint256,CCoins> CCoinsMap;
+class CCoinsKeyHasher
+{
+private:
+ uint256 salt;
+
+public:
+ CCoinsKeyHasher();
+ uint64_t operator()(const uint256& key) const {
+ return key.GetHash(salt);
+ }
+};
+
+typedef boost::unordered_map<uint256, CCoins, CCoinsKeyHasher> CCoinsMap;
struct CCoinsStats
{
diff --git a/src/compat.h b/src/compat.h
index 8fbafb6cce..1b3a60d11b 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -59,19 +59,4 @@ typedef u_int SOCKET;
#define SOCKET_ERROR -1
#endif
-inline int myclosesocket(SOCKET& hSocket)
-{
- if (hSocket == INVALID_SOCKET)
- return WSAENOTSOCK;
-#ifdef WIN32
- int ret = closesocket(hSocket);
-#else
- int ret = close(hSocket);
-#endif
- hSocket = INVALID_SOCKET;
- return ret;
-}
-#define closesocket(s) myclosesocket(s)
-
-
#endif
diff --git a/src/core.cpp b/src/core.cpp
index b56994ecf3..149b3532a1 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -124,22 +124,6 @@ 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 0387336c98..fb64e6c08e 100644
--- a/src/core.h
+++ b/src/core.h
@@ -255,9 +255,6 @@ 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 a1d75c9674..14816f5016 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -58,7 +58,8 @@ CWallet* pwalletMain;
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
- BF_REPORT_ERROR = (1U << 1)
+ BF_REPORT_ERROR = (1U << 1),
+ BF_WHITELIST = (1U << 2),
};
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
@@ -126,12 +127,14 @@ void Shutdown()
StopNode();
UnregisterNodeSignals(GetNodeSignals());
- boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
- CAutoFile est_fileout = CAutoFile(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
- if (est_fileout)
- mempool.WriteFeeEstimates(est_fileout);
- else
- LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
+ {
+ boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
+ if (est_fileout)
+ mempool.WriteFeeEstimates(est_fileout);
+ else
+ LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
+ }
{
LOCK(cs_main);
@@ -178,13 +181,13 @@ void HandleSIGHUP(int)
bool static InitError(const std::string &str)
{
- uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::NOSHOWGUI);
+ uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR);
return false;
}
bool static InitWarning(const std::string &str)
{
- uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING | CClientUIInterface::NOSHOWGUI);
+ uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
return true;
}
@@ -192,7 +195,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError)) {
+ if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) {
if (flags & BF_REPORT_ERROR)
return InitError(strError);
return false;
@@ -218,14 +221,17 @@ std::string HelpMessage(HelpMessageMode mode)
}
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n";
- strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n";
strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n";
strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n";
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup") + "\n";
+#if !defined(WIN32)
+ strUsage += " -sysperms " + _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)") + "\n";
+#endif
strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n";
+
strUsage += "\n" + _("Connection options:") + "\n";
strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n";
strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n";
@@ -234,7 +240,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n";
strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n";
strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + _("(default: 1)") + "\n";
- strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n";
+ strUsage += " -dnsseed " + _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)") + "\n";
+ strUsage += " -forcednsseed " + _("Always query for peer addresses via DNS lookup (default: 0)") + "\n";
strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n";
strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n";
strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n";
@@ -242,6 +249,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n";
strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n";
strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n";
+ strUsage += " -permitbaremultisig " + _("Relay non-P2SH multisig (default: 1)") + "\n";
strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n";
strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n";
strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n";
@@ -253,10 +261,14 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
#endif
#endif
+ strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
+ strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
+ strUsage += " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway") + "\n";
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n";
+ strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
if (GetBoolArg("-help-debug", false))
strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n";
strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n";
@@ -292,8 +304,10 @@ std::string HelpMessage(HelpMessageMode mode)
if (mode == HMM_BITCOIN_QT)
strUsage += ", qt";
strUsage += ".\n";
+#ifdef ENABLE_WALLET
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n";
+#endif
strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n";
strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n";
strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n";
@@ -479,7 +493,15 @@ bool AppInit2(boost::thread_group& threadGroup)
}
#endif
#ifndef WIN32
- umask(077);
+
+ if (GetBoolArg("-sysperms", false)) {
+#ifdef ENABLE_WALLET
+ if (!GetBoolArg("-disablewallet", false))
+ return InitError("Error: -sysperms is not allowed in combination with enabled wallet functionality");
+#endif
+ } else {
+ umask(077);
+ }
// Clean shutdown on SIGTERM
struct sigaction sa;
@@ -504,11 +526,11 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 2: parameter interactions
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (SoftSetBoolArg("-listen", true))
- LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n");
+ LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
}
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@@ -552,7 +574,7 @@ bool AppInit2(boost::thread_group& threadGroup)
}
// Make sure enough file descriptors are available
- int nBind = std::max((int)mapArgs.count("-bind"), 1);
+ int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@@ -657,7 +679,10 @@ bool AppInit2(boost::thread_group& threadGroup)
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
-#endif
+#endif // ENABLE_WALLET
+
+ fIsBareMultisigStd = GetArg("-permitbaremultisig", true);
+
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
// Sanity check
if (!InitSanityCheck())
@@ -769,6 +794,15 @@ bool AppInit2(boost::thread_group& threadGroup)
}
}
+ if (mapArgs.count("-whitelist")) {
+ BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
+ CSubNet subnet(net);
+ if (!subnet.IsValid())
+ return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
+ CNode::AddWhitelistedRange(subnet);
+ }
+ }
+
CService addrProxy;
bool fProxy = false;
if (mapArgs.count("-proxy")) {
@@ -805,13 +839,21 @@ bool AppInit2(boost::thread_group& threadGroup)
bool fBound = false;
if (fListen) {
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
+ BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false))
+ return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
+ if (addrBind.GetPort() == 0)
+ return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ }
}
else {
struct in_addr inaddr_any;
@@ -1189,7 +1231,6 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif
- InitRespendFilter();
StartNode(threadGroup);
if (fServer)
StartRPCThreads();
diff --git a/src/key.cpp b/src/key.cpp
index 3c4fa77e72..a253f8666a 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2009-2013 The Bitcoin developers
+// 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 "key.h"
#include "crypto/sha2.h"
-#include <openssl/rand.h>
+#include "random.h"
#ifdef USE_SECP256K1
#include <secp256k1.h>
@@ -194,7 +194,7 @@ public:
if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
if(fSkipCheck)
return true;
-
+
// d2i_ECPrivateKey returns true if parsing succeeds.
// This doesn't necessarily mean the key is valid.
if (EC_KEY_check_key(pkey))
@@ -412,7 +412,7 @@ bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) {
void CKey::MakeNewKey(bool fCompressedIn) {
do {
- RAND_bytes(vch, sizeof(vch));
+ GetRandBytes(vch, sizeof(vch));
} while (!Check(vch));
fValid = true;
fCompressed = fCompressedIn;
@@ -745,5 +745,3 @@ bool ECC_InitSanityCheck() {
return true;
#endif
}
-
-
diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp
index 5b4a9c147b..9e849696a8 100644
--- a/src/leveldbwrapper.cpp
+++ b/src/leveldbwrapper.cpp
@@ -32,6 +32,11 @@ static leveldb::Options GetOptions(size_t nCacheSize) {
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.compression = leveldb::kNoCompression;
options.max_open_files = 64;
+ if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
+ // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error
+ // on corruption in later versions.
+ options.paranoid_checks = true;
+ }
return options;
}
diff --git a/src/m4/bitcoin_qt.m4 b/src/m4/bitcoin_qt.m4
index 244b03a5c2..9356aac37f 100644
--- a/src/m4/bitcoin_qt.m4
+++ b/src/m4/bitcoin_qt.m4
@@ -86,14 +86,68 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
fi
if test x$use_pkgconfig = xyes; then
- if test x$PKG_CONFIG == x; then
- AC_MSG_ERROR(pkg-config not found.)
- fi
BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG([$2])])
else
BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG])
fi
+ dnl This is ugly and complicated. Yuck. Works as follows:
+ dnl We can't discern whether Qt4 builds are static or not. For Qt5, we can
+ dnl check a header to find out. When Qt is built statically, some plugins must
+ dnl be linked into the final binary as well. These plugins have changed between
+ dnl Qt4 and Qt5. With Qt5, languages moved into core and the WindowsIntegration
+ dnl plugin was added. Since we can't tell if Qt4 is static or not, it is
+ dnl assumed for windows builds.
+ dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the
+ dnl results to QT_LIBS.
+ BITCOIN_QT_CHECK([
+ TEMP_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS=$QT_INCLUDES
+ if test x$bitcoin_qt_got_major_vers == x5; then
+ _BITCOIN_QT_IS_STATIC
+ if test x$bitcoin_cv_static_qt == xyes; then
+ AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
+ if test x$qt_plugin_path != x; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible"
+ if test x$bitcoin_qt_got_major_vers == x5; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms"
+ else
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs"
+ fi
+ fi
+ if test x$use_pkgconfig = xyes; then
+ PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"])
+ fi
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets])
+ if test x$TARGET_OS == xwindows; then
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
+ AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
+ elif test x$TARGET_OS == xlinux; then
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static -lxcb])
+ AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb])
+ elif test x$TARGET_OS == xdarwin; then
+ if test x$use_pkgconfig = xyes; then
+ PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"])
+ fi
+ AX_CHECK_LINK_FLAG([[-framework IOKit]],[QT_LIBS="$QT_LIBS -framework IOKit"],[AC_MSG_ERROR(could not iokit framework)])
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa])
+ AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa])
+ fi
+ fi
+ else
+ if test x$TARGET_OS == xwindows; then
+ AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([
+ Q_IMPORT_PLUGIN(qcncodecs)
+ Q_IMPORT_PLUGIN(qjpcodecs)
+ Q_IMPORT_PLUGIN(qtwcodecs)
+ Q_IMPORT_PLUGIN(qkrcodecs)
+ Q_IMPORT_PLUGIN(AccessibleFactory)],
+ [-lqcncodecs -lqjpcodecs -lqtwcodecs -lqkrcodecs -lqtaccessiblewidgets])
+ fi
+ fi
+ CPPFLAGS=$TEMP_CPPFLAGS
+ ])
BITCOIN_QT_PATH_PROGS([MOC], [moc-qt${bitcoin_qt_got_major_vers} moc${bitcoin_qt_got_major_vers} moc], $qt_bin_path)
BITCOIN_QT_PATH_PROGS([UIC], [uic-qt${bitcoin_qt_got_major_vers} uic${bitcoin_qt_got_major_vers} uic], $qt_bin_path)
BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt${bitcoin_qt_got_major_vers} rcc${bitcoin_qt_got_major_vers} rcc], $qt_bin_path)
@@ -303,26 +357,15 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
])
BITCOIN_QT_CHECK([
- LIBS=
- if test x$qt_lib_path != x; then
- LIBS="$LIBS -L$qt_lib_path"
- fi
- if test x$qt_plugin_path != x; then
- LIBS="$LIBS -L$qt_plugin_path/accessible"
- if test x$bitcoin_qt_got_major_vers == x5; then
- LIBS="$LIBS -L$qt_plugin_path/platforms"
- else
- LIBS="$LIBS -L$qt_plugin_path/codecs"
- fi
- fi
-
if test x$TARGET_OS == xwindows; then
AC_CHECK_LIB([imm32], [main],, BITCOIN_QT_FAIL(libimm32 not found))
fi
])
- BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,BITCOIN_QT_FAIL(zlib not found)))
- BITCOIN_QT_CHECK(AC_CHECK_LIB([png] ,[main],,BITCOIN_QT_FAIL(png not found)))
+ BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,AC_MSG_WARN([zlib not found. Assuming qt has it built-in])))
+ BITCOIN_QT_CHECK(AC_CHECK_LIB([png] ,[main],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in])))
+ BITCOIN_QT_CHECK(AC_CHECK_LIB([jpeg] ,[main],,AC_MSG_WARN([libjpeg not found. Assuming qt has it built-in])))
+ BITCOIN_QT_CHECK(AC_CHECK_LIB([pcre] ,[main],,AC_MSG_WARN([libpcre not found. Assuming qt has it built-in])))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Core] ,[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXCore not found)))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Gui] ,[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXGui not found)))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Network],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXNetwork not found)))
@@ -332,37 +375,6 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
QT_LIBS="$LIBS"
LIBS="$TEMP_LIBS"
- dnl This is ugly and complicated. Yuck. Works as follows:
- dnl We can't discern whether Qt4 builds are static or not. For Qt5, we can
- dnl check a header to find out. When Qt is built statically, some plugins must
- dnl be linked into the final binary as well. These plugins have changed between
- dnl Qt4 and Qt5. With Qt5, languages moved into core and the WindowsIntegration
- dnl plugin was added. Since we can't tell if Qt4 is static or not, it is
- dnl assumed for all non-pkg-config builds.
- dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the
- dnl results to QT_LIBS.
- BITCOIN_QT_CHECK([
- if test x$bitcoin_qt_got_major_vers == x5; then
- _BITCOIN_QT_IS_STATIC
- if test x$bitcoin_cv_static_qt == xyes; then
- AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets])
- if test x$TARGET_OS == xwindows; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
- fi
- fi
- else
- AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([
- Q_IMPORT_PLUGIN(qcncodecs)
- Q_IMPORT_PLUGIN(qjpcodecs)
- Q_IMPORT_PLUGIN(qtwcodecs)
- Q_IMPORT_PLUGIN(qkrcodecs)
- Q_IMPORT_PLUGIN(AccessibleFactory)],
- [-lqcncodecs -lqjpcodecs -lqtwcodecs -lqkrcodecs -lqtaccessiblewidgets])
- fi
- ])
-
BITCOIN_QT_CHECK([
LIBS=
if test x$qt_lib_path != x; then
diff --git a/src/main.cpp b/src/main.cpp
index a9c080ffae..06ce15b5b3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -7,7 +7,6 @@
#include "addrman.h"
#include "alert.h"
-#include "bloom.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
@@ -41,11 +40,14 @@ CCriticalSection cs_main;
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
int64_t nTimeBestReceived = 0;
+CWaitableCriticalSection csBestBlock;
+CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fBenchmark = false;
bool fTxIndex = false;
+bool fIsBareMultisigStd = true;
unsigned int nCoinCacheSize = 5000;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
@@ -123,15 +125,6 @@ namespace {
} // anon namespace
-// Bloom filter to limit respend relays to one
-static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
-static CBloomFilter doubleSpendFilter;
-void InitRespendFilter() {
- seed_insecure_rand();
- doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
-}
-
-
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
@@ -158,7 +151,6 @@ struct CMainSignals {
} // anon namespace
-
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));
@@ -210,7 +202,7 @@ struct CBlockReject {
struct CNodeState {
// Accumulated misbehaviour score for this peer.
int nMisbehavior;
- // Whether this peer should be disconnected and banned.
+ // Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan;
// String name of this peer (debugging/logging purposes).
std::string name;
@@ -602,9 +594,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
reason = "scriptpubkey";
return false;
}
+
if (whichType == TX_NULL_DATA)
nDataOut++;
- else if (txout.IsDust(::minRelayTxFee)) {
+ else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
+ reason = "bare-multisig";
+ return false;
+ } else if (txout.IsDust(::minRelayTxFee)) {
reason = "dust";
return false;
}
@@ -874,60 +870,6 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
return nMinFee;
}
-// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute.
-bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize)
-{
- static CCriticalSection csLimiter;
- int64_t nNow = GetTime();
-
- LOCK(csLimiter);
-
- dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
- nLastTime = nNow;
- if (dCount >= nLimit*10*1000)
- return true;
- dCount += nSize;
- return false;
-}
-
-static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
-{
- // Relaying double-spend attempts to our peers lets them detect when
- // somebody might be trying to cheat them. However, blindly relaying
- // every double-spend across the entire network gives attackers
- // a denial-of-service attack: just generate a stream of double-spends
- // re-spending the same (limited) set of outpoints owned by the attacker.
- // So, we use a bloom filter and only relay (at most) the first double
- // spend for each outpoint. False-positives ("we have already relayed")
- // are OK, because if the peer doesn't hear about the double-spend
- // from us they are very likely to hear about it from another peer, since
- // each peer uses a different, randomized bloom filter.
-
- if (fInBlock || filter.contains(outPoint)) return false;
-
- // Apply an independent rate limit to double-spend relays
- static double dRespendCount;
- static int64_t nLastRespendTime;
- static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
- unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
-
- if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
- {
- LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
- return false;
- }
-
- LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
-
- // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
- // insertions
- if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
- filter.clear();
-
- filter.insert(outPoint);
-
- return true;
-}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee)
@@ -957,18 +899,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return false;
// Check for conflicts with in-memory transactions
- bool relayableRespend = false;
{
LOCK(pool.cs); // protect pool.mapNextTx
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
COutPoint outpoint = tx.vin[i].prevout;
- // 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))
+ if (pool.mapNextTx.count(outpoint))
{
- relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter);
- if (!relayableRespend)
- return false;
+ // Disable replacement feature for now
+ return false;
}
}
}
@@ -1039,15 +978,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
{
+ static CCriticalSection csFreeLimiter;
static double dFreeCount;
- static int64_t nLastFreeTime;
- static int64_t nFreeLimit = GetArg("-limitfreerelay", 15);
+ static int64_t nLastTime;
+ int64_t nNow = GetTime();
+
+ LOCK(csFreeLimiter);
- if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize))
+ // 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)
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 > ::minRelayTxFee.GetFee(nSize) * 10000)
@@ -1061,21 +1008,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
{
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
}
-
- if (relayableRespend)
- {
- RelayTransaction(tx);
- }
- else
- {
- // Store transaction in memory
- pool.addUnchecked(hash, entry);
- }
+ // Store transaction in memory
+ pool.addUnchecked(hash, entry);
}
g_signals.SyncTransaction(tx, NULL);
- return !relayableRespend;
+ return true;
}
@@ -1425,7 +1364,8 @@ void Misbehaving(NodeId pnode, int howmuch)
return;
state->nMisbehavior += howmuch;
- if (state->nMisbehavior >= GetArg("-banscore", 100))
+ int banscore = GetArg("-banscore", 100);
+ if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
state->fShouldBan = true;
@@ -1915,7 +1855,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
// Update the on-disk chain state.
bool static WriteChainState(CValidationState &state) {
static int64_t nLastWrite = 0;
- if (!IsInitialBlockDownload() || pcoinsTip->GetCacheSize() > nCoinCacheSize || GetTimeMicros() > nLastWrite + 600*1000000) {
+ if (pcoinsTip->GetCacheSize() > nCoinCacheSize || (!IsInitialBlockDownload() && GetTimeMicros() > nLastWrite + 600*1000000)) {
// Typical CCoins structures on disk are around 100 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
@@ -1944,11 +1884,14 @@ void static UpdateTip(CBlockIndex *pindexNew) {
// New best block
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
+
LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip()));
+ cvBlockChange.notify_all();
+
// Check the version of the last 100 blocks to see if we need to upgrade:
if (!fIsInitialDownload)
{
@@ -2073,7 +2016,7 @@ static CBlockIndex* FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
- if (!pindexTest->IsValid(BLOCK_VALID_TRANSACTIONS) || !(pindexTest->nStatus & BLOCK_HAVE_DATA)) {
+ if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
// Candidate has an invalid ancestor, remove entire chain from the set.
if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
@@ -2083,6 +2026,7 @@ static CBlockIndex* FindMostWorkChain() {
setBlockIndexValid.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
+ setBlockIndexValid.erase(pindexTest);
fInvalidAncestor = true;
break;
}
@@ -2511,7 +2455,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
return false;
if (!CheckBlock(block, state)) {
- if (state.Invalid() && !state.CorruptionPossible()) {
+ if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
}
return false;
@@ -3947,6 +3891,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
+ } else if (pfrom->fWhitelisted) {
+ // Always relay transactions received from whitelisted peers, even
+ // if they are already in the mempool (allowing the node to function
+ // as a gateway for nodes hidden behind it).
+ RelayTransaction(tx);
}
int nDoS = 0;
if (state.IsInvalid(nDoS))
@@ -4370,7 +4319,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if (pingSend) {
uint64_t nonce = 0;
while (nonce == 0) {
- RAND_bytes((unsigned char*)&nonce, sizeof(nonce));
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
}
pto->fPingQueued = false;
pto->nPingUsecStart = GetTimeMicros();
@@ -4440,11 +4389,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
- if (pto->addr.IsLocal())
- LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
+ if (pto->fWhitelisted)
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
else {
pto->fDisconnect = true;
- CNode::Ban(pto->addr);
+ if (pto->addr.IsLocal())
+ LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
+ else
+ CNode::Ban(pto->addr);
}
state.fShouldBan = false;
}
diff --git a/src/main.h b/src/main.h
index f6bac889be..5f231fa45b 100644
--- a/src/main.h
+++ b/src/main.h
@@ -87,11 +87,14 @@ extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern const std::string strMessageMagic;
extern int64_t nTimeBestReceived;
+extern CWaitableCriticalSection csBestBlock;
+extern CConditionVariable cvBlockChange;
extern bool fImporting;
extern bool fReindex;
extern bool fBenchmark;
extern int nScriptCheckThreads;
extern bool fTxIndex;
+extern bool fIsBareMultisigStd;
extern unsigned int nCoinCacheSize;
extern CFeeRate minRelayTxFee;
@@ -109,9 +112,6 @@ struct CNodeStateStats;
struct CBlockTemplate;
-/** Initialize respend bloom filter **/
-void InitRespendFilter();
-
/** Register a wallet to receive updates from core */
void RegisterWallet(CWalletInterface* pwalletIn);
/** Unregister a wallet from core */
diff --git a/src/net.cpp b/src/net.cpp
index 6a660dc9bd..62124514c8 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -50,7 +50,16 @@
using namespace std;
using namespace boost;
-static const int MAX_OUTBOUND_CONNECTIONS = 8;
+namespace {
+ const int MAX_OUTBOUND_CONNECTIONS = 8;
+
+ struct ListenSocket {
+ SOCKET socket;
+ bool whitelisted;
+
+ ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
+ };
+}
//
// Global state variables
@@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
static CNode* pnodeSync = NULL;
uint64_t nLocalHostNonce = 0;
-static std::vector<SOCKET> vhListenSocket;
+static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
@@ -323,7 +332,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
{
if (!RecvLine(hSocket, strLine))
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
if (pszKeyword == NULL)
@@ -334,7 +343,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
break;
}
}
- closesocket(hSocket);
+ CloseSocket(hSocket);
if (strLine.find("<") != string::npos)
strLine = strLine.substr(0, strLine.find("<"));
strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
@@ -348,7 +357,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
return true;
}
}
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("GetMyExternalIP() : connection closed");
}
@@ -447,7 +456,7 @@ CNode* FindNode(const CNetAddr& ip)
return NULL;
}
-CNode* FindNode(std::string addrName)
+CNode* FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -492,14 +501,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
addrman.Attempt(addrConnect);
// Set to non-blocking
-#ifdef WIN32
- u_long nOne = 1;
- if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
- LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %s\n", NetworkErrorString(WSAGetLastError()));
-#else
- if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
- LogPrintf("ConnectSocket() : fcntl non-blocking setting failed, error %s\n", NetworkErrorString(errno));
-#endif
+ if (!SetSocketNonBlocking(hSocket, true))
+ LogPrintf("ConnectNode: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
// Add node
CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
@@ -524,8 +527,7 @@ void CNode::CloseSocketDisconnect()
if (hSocket != INVALID_SOCKET)
{
LogPrint("net", "disconnecting peer=%d\n", id);
- closesocket(hSocket);
- hSocket = INVALID_SOCKET;
+ CloseSocket(hSocket);
}
// in case this fails, we'll empty the recv buffer when the CNode is deleted
@@ -546,7 +548,7 @@ void CNode::PushVersion()
int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
CAddress addrMe = GetLocalAddress(&addr);
- RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
+ GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
if (fLogIPs)
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
else
@@ -593,6 +595,24 @@ bool CNode::Ban(const CNetAddr &addr) {
return true;
}
+
+std::vector<CSubNet> CNode::vWhitelistedRange;
+CCriticalSection CNode::cs_vWhitelistedRange;
+
+bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
+ LOCK(cs_vWhitelistedRange);
+ BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
+ if (subnet.Match(addr))
+ return true;
+ }
+ return false;
+}
+
+void CNode::AddWhitelistedRange(const CSubNet &subnet) {
+ LOCK(cs_vWhitelistedRange);
+ vWhitelistedRange.push_back(subnet);
+}
+
#undef X
#define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats)
@@ -609,6 +629,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nStartingHeight);
X(nSendBytes);
X(nRecvBytes);
+ X(fWhitelisted);
stats.fSyncNode = (this == pnodeSync);
// It is common for nodes with good ping times to suddenly become lagged,
@@ -848,9 +869,9 @@ void ThreadSocketHandler()
SOCKET hSocketMax = 0;
bool have_fds = false;
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) {
- FD_SET(hListenSocket, &fdsetRecv);
- hSocketMax = max(hSocketMax, hListenSocket);
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
+ FD_SET(hListenSocket.socket, &fdsetRecv);
+ hSocketMax = max(hSocketMax, hListenSocket.socket);
have_fds = true;
}
@@ -917,13 +938,13 @@ void ThreadSocketHandler()
//
// Accept new connections
//
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
{
- if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
+ if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
+ SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
@@ -931,6 +952,7 @@ void ThreadSocketHandler()
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n");
+ bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -946,17 +968,18 @@ void ThreadSocketHandler()
}
else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
}
- else if (CNode::IsBanned(addr))
+ else if (CNode::IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
- closesocket(hSocket);
+ CloseSocket(hSocket);
}
else
{
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
+ pnode->fWhitelisted = whitelisted;
{
LOCK(cs_vNodes);
@@ -1198,6 +1221,18 @@ void MapPort(bool)
void ThreadDNSAddressSeed()
{
+ // goal: only query DNS seeds if address need is acute
+ if ((addrman.size() > 0) &&
+ (!GetBoolArg("-forcednsseed", false))) {
+ MilliSleep(11 * 1000);
+
+ LOCK(cs_vNodes);
+ if (vNodes.size() >= 2) {
+ LogPrintf("P2P peers available. Skipped DNS seeding.\n");
+ return;
+ }
+ }
+
const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds();
int found = 0;
@@ -1449,7 +1484,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
- FindNode(addrConnect.ToStringIPPort().c_str()))
+ FindNode(addrConnect.ToStringIPPort()))
return false;
} else if (FindNode(pszDest))
return false;
@@ -1580,7 +1615,7 @@ void ThreadMessageHandler()
-bool BindListenPort(const CService &addrBind, string& strError)
+bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
{
strError = "";
int nOne = 1;
@@ -1613,14 +1648,9 @@ bool BindListenPort(const CService &addrBind, string& strError)
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
#endif
-#ifdef WIN32
// Set to non-blocking, incoming connections will also inherit this
- if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
-#else
- if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
-#endif
- {
- strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %s)", NetworkErrorString(WSAGetLastError()));
+ if (!SetSocketNonBlocking(hListenSocket, true)) {
+ strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
}
@@ -1649,6 +1679,7 @@ bool BindListenPort(const CService &addrBind, string& strError)
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
LogPrintf("%s\n", strError);
+ CloseSocket(hListenSocket);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
@@ -1658,12 +1689,13 @@ bool BindListenPort(const CService &addrBind, string& strError)
{
strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
+ CloseSocket(hListenSocket);
return false;
}
- vhListenSocket.push_back(hListenSocket);
+ vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
- if (addrBind.IsRoutable() && fDiscover)
+ if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -1787,11 +1819,11 @@ public:
// Close sockets
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET)
- closesocket(pnode->hSocket);
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
- if (hListenSocket != INVALID_SOCKET)
- if (closesocket(hListenSocket) == SOCKET_ERROR)
- LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
+ CloseSocket(pnode->hSocket);
+ BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
+ if (hListenSocket.socket != INVALID_SOCKET)
+ if (!CloseSocket(hListenSocket.socket))
+ LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection)
BOOST_FOREACH(CNode *pnode, vNodes)
@@ -1931,7 +1963,7 @@ bool CAddrDB::Write(const CAddrMan& addr)
{
// Generate random temporary filename
unsigned short randv = 0;
- RAND_bytes((unsigned char *)&randv, sizeof(randv));
+ GetRandBytes((unsigned char*)&randv, sizeof(randv));
std::string tmpfn = strprintf("peers.dat.%04x", randv);
// serialize addresses, checksum data up to that point, then append csum
diff --git a/src/net.h b/src/net.h
index c2a0416455..2d9325abf9 100644
--- a/src/net.h
+++ b/src/net.h
@@ -13,6 +13,7 @@
#include "mruset.h"
#include "netbase.h"
#include "protocol.h"
+#include "random.h"
#include "sync.h"
#include "uint256.h"
#include "util.h"
@@ -26,7 +27,6 @@
#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
-#include <openssl/rand.h>
class CAddrMan;
class CBlockIndex;
@@ -59,12 +59,13 @@ bool RecvLine(SOCKET hSocket, std::string& strLine);
bool GetMyExternalIP(CNetAddr& ipRet);
void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip);
+CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
void MapPort(bool fUseUPnP);
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError);
+bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
void StartNode(boost::thread_group& threadGroup);
bool StopNode();
void SocketSendData(CNode *pnode);
@@ -154,6 +155,7 @@ public:
uint64_t nSendBytes;
uint64_t nRecvBytes;
bool fSyncNode;
+ bool fWhitelisted;
double dPingTime;
double dPingWait;
std::string addrLocal;
@@ -236,6 +238,7 @@ public:
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
+ bool fWhitelisted; // This peer can bypass DoS banning.
bool fOneShot;
bool fClient;
bool fInbound;
@@ -259,6 +262,11 @@ protected:
static std::map<CNetAddr, int64_t> setBanned;
static CCriticalSection cs_setBanned;
+ // Whitelisted ranges. Any node connecting from these is automatically
+ // whitelisted (as well as those connecting to whitelisted binds).
+ static std::vector<CSubNet> vWhitelistedRange;
+ static CCriticalSection cs_vWhitelistedRange;
+
// Basic fuzz-testing
void Fuzz(int nChance); // modifies ssSend
@@ -305,6 +313,7 @@ public:
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
strSubVer = "";
+ fWhitelisted = false;
fOneShot = false;
fClient = false; // set by version message
fInbound = fInboundIn;
@@ -349,8 +358,7 @@ public:
{
if (hSocket != INVALID_SOCKET)
{
- closesocket(hSocket);
- hSocket = INVALID_SOCKET;
+ CloseSocket(hSocket);
}
if (pfilter)
delete pfilter;
@@ -720,6 +728,9 @@ public:
static bool Ban(const CNetAddr &ip);
void copyStats(CNodeStats &stats);
+ static bool IsWhitelistedRange(const CNetAddr &ip);
+ static void AddWhitelistedRange(const CSubNet &subnet);
+
// Network stats
static void RecordBytesRecv(uint64_t bytes);
static void RecordBytesSent(uint64_t bytes);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 067cfa024b..af6d11f0e2 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -7,10 +7,6 @@
#include "bitcoin-config.h"
#endif
-#ifdef HAVE_GETADDRINFO_A
-#include <netdb.h>
-#endif
-
#include "netbase.h"
#include "hash.h"
@@ -18,6 +14,10 @@
#include "uint256.h"
#include "util.h"
+#ifdef HAVE_GETADDRINFO_A
+#include <netdb.h>
+#endif
+
#ifndef WIN32
#if HAVE_INET_PTON
#include <arpa/inet.h>
@@ -218,7 +218,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
LogPrintf("SOCKS5 connecting %s\n", strDest);
if (strDest.size() > 255)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Hostname too long");
}
char pszSocks5Init[] = "\5\1\0";
@@ -227,18 +227,18 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL);
if (ret != nSize)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error sending to proxy");
}
char pchRet1[2];
if (recv(hSocket, pchRet1, 2, 0) != 2)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error reading proxy response");
}
if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Proxy failed to initialize");
}
string strSocks5("\5\1");
@@ -250,23 +250,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)strSocks5.size())
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error sending to proxy");
}
char pchRet2[4];
if (recv(hSocket, pchRet2, 4, 0) != 4)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error reading proxy response");
}
if (pchRet2[0] != 0x05)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Proxy failed to accept request");
}
if (pchRet2[1] != 0x00)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
switch (pchRet2[1])
{
case 0x01: return error("Proxy error: general failure");
@@ -282,7 +282,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
}
if (pchRet2[2] != 0x00)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error: malformed proxy response");
}
char pchRet3[256];
@@ -294,23 +294,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
{
ret = recv(hSocket, pchRet3, 1, 0) != 1;
if (ret) {
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv;
break;
}
- default: closesocket(hSocket); return error("Error: malformed proxy response");
+ default: CloseSocket(hSocket); return error("Error: malformed proxy response");
}
if (ret)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error reading from proxy");
}
if (recv(hSocket, pchRet3, 2, 0) != 2)
{
- closesocket(hSocket);
+ CloseSocket(hSocket);
return error("Error reading from proxy");
}
LogPrintf("SOCKS5 connected %s\n", strDest);
@@ -331,22 +331,15 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
return false;
+
#ifdef SO_NOSIGPIPE
int set = 1;
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
#endif
-#ifdef WIN32
- u_long fNonblock = 1;
- if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
-#else
- int fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1)
-#endif
- {
- closesocket(hSocket);
- return false;
- }
+ // Set to non-blocking
+ if (!SetSocketNonBlocking(hSocket, true))
+ return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
@@ -365,13 +358,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
if (nRet == 0)
{
LogPrint("net", "connection to %s timeout\n", addrConnect.ToString());
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
if (nRet == SOCKET_ERROR)
{
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
socklen_t nRetSize = sizeof(nRet);
@@ -382,13 +375,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
#endif
{
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
if (nRet != 0)
{
LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
}
@@ -399,25 +392,15 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
#endif
{
LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
- closesocket(hSocket);
+ CloseSocket(hSocket);
return false;
}
}
- // this isn't even strictly necessary
- // CNode::ConnectNode immediately turns the socket back to non-blocking
- // but we'll turn it back to blocking just in case
-#ifdef WIN32
- fNonblock = 0;
- if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
-#else
- fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR)
-#endif
- {
- closesocket(hSocket);
- return false;
- }
+ // This is required when using SOCKS5 proxy!
+ // CNode::ConnectNode turns the socket back to non-blocking.
+ if (!SetSocketNonBlocking(hSocket, false))
+ return error("ConnectSocketDirectly: Setting socket to blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
hSocketRet = hSocket;
return true;
@@ -1258,3 +1241,45 @@ std::string NetworkErrorString(int err)
return strprintf("%s (%d)", s, err);
}
#endif
+
+bool CloseSocket(SOCKET& hSocket)
+{
+ if (hSocket == INVALID_SOCKET)
+ return false;
+#ifdef WIN32
+ int ret = closesocket(hSocket);
+#else
+ int ret = close(hSocket);
+#endif
+ hSocket = INVALID_SOCKET;
+ return ret != SOCKET_ERROR;
+}
+
+bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
+{
+ if (fNonBlocking) {
+#ifdef WIN32
+ u_long nOne = 1;
+ if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
+#else
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
+#endif
+ CloseSocket(hSocket);
+ return false;
+ }
+ } else {
+#ifdef WIN32
+ u_long nZero = 0;
+ if (ioctlsocket(hSocket, FIONBIO, &nZero) == SOCKET_ERROR) {
+#else
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) {
+#endif
+ CloseSocket(hSocket);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/netbase.h b/src/netbase.h
index ad1e230834..7d83e35344 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -178,5 +178,9 @@ bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nCon
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout);
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
+/** Close socket and set hSocket to INVALID_SOCKET */
+bool CloseSocket(SOCKET& hSocket);
+/** Disable or enable blocking-mode for a socket */
+bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
#endif
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 5df8f19729..f336d47e83 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -282,7 +282,7 @@ void AddressBookPage::on_exportButton_clicked()
if(!writer.write()) {
QMessageBox::critical(this, tr("Exporting Failed"),
- tr("There was an error trying to save the address list to %1.").arg(filename));
+ tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
}
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 7c4af25edf..7bf531f538 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -34,6 +34,7 @@
#include <boost/filesystem/operations.hpp>
#include <QApplication>
+#include <QDebug>
#include <QLibraryInfo>
#include <QLocale>
#include <QMessageBox>
@@ -52,7 +53,13 @@ Q_IMPORT_PLUGIN(qkrcodecs)
Q_IMPORT_PLUGIN(qtaccessiblewidgets)
#else
Q_IMPORT_PLUGIN(AccessibleFactory)
+#if defined(QT_QPA_PLATFORM_XCB)
+Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
+#elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
+#elif defined(QT_QPA_PLATFORM_COCOA)
+Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
+#endif
#endif
#endif
@@ -237,7 +244,7 @@ void BitcoinCore::initialize()
{
try
{
- LogPrintf("Running AppInit2 in thread\n");
+ qDebug() << __func__ << ": Running AppInit2 in thread";
int rv = AppInit2(threadGroup);
if(rv)
{
@@ -258,11 +265,11 @@ void BitcoinCore::shutdown()
{
try
{
- LogPrintf("Running Shutdown in thread\n");
+ qDebug() << __func__ << ": Running Shutdown in thread";
threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();
- LogPrintf("Shutdown finished\n");
+ qDebug() << __func__ << ": Shutdown finished";
emit shutdownResult(1);
} catch (std::exception& e) {
handleRunawayException(&e);
@@ -285,15 +292,17 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv):
returnValue(0)
{
setQuitOnLastWindowClosed(false);
- startThread();
}
BitcoinApplication::~BitcoinApplication()
{
- LogPrintf("Stopping thread\n");
- emit stopThread();
- coreThread->wait();
- LogPrintf("Stopped thread\n");
+ if(coreThread)
+ {
+ qDebug() << __func__ << ": Stopping thread";
+ emit stopThread();
+ coreThread->wait();
+ qDebug() << __func__ << ": Stopped thread";
+ }
delete window;
window = 0;
@@ -336,6 +345,8 @@ void BitcoinApplication::createSplashScreen(bool isaTestNet)
void BitcoinApplication::startThread()
{
+ if(coreThread)
+ return;
coreThread = new QThread(this);
BitcoinCore *executor = new BitcoinCore();
executor->moveToThread(coreThread);
@@ -355,13 +366,15 @@ void BitcoinApplication::startThread()
void BitcoinApplication::requestInitialize()
{
- LogPrintf("Requesting initialize\n");
+ qDebug() << __func__ << ": Requesting initialize";
+ startThread();
emit requestedInitialize();
}
void BitcoinApplication::requestShutdown()
{
- LogPrintf("Requesting shutdown\n");
+ qDebug() << __func__ << ": Requesting shutdown";
+ startThread();
window->hide();
window->setClientModel(0);
pollShutdownTimer->stop();
@@ -383,7 +396,7 @@ void BitcoinApplication::requestShutdown()
void BitcoinApplication::initializeResult(int retval)
{
- LogPrintf("Initialization result: %i\n", retval);
+ qDebug() << __func__ << ": Initialization result: " << retval;
// Set exit result: 0 if successful, 1 if failure
returnValue = retval ? 0 : 1;
if(retval)
@@ -393,8 +406,6 @@ void BitcoinApplication::initializeResult(int retval)
paymentServer->setOptionsModel(optionsModel);
#endif
- emit splashFinished(window);
-
clientModel = new ClientModel(optionsModel);
window->setClientModel(clientModel);
@@ -411,6 +422,8 @@ void BitcoinApplication::initializeResult(int retval)
}
#endif
+ emit splashFinished(window);
+
// If -min option passed, start window minimized.
if(GetBoolArg("-min", false))
{
@@ -438,7 +451,7 @@ void BitcoinApplication::initializeResult(int retval)
void BitcoinApplication::shutdownResult(int retval)
{
- LogPrintf("Shutdown result: %i\n", retval);
+ qDebug() << __func__ << ": Shutdown result: " << retval;
quit(); // Exit main loop after shutdown finished
}
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 25ad0c66af..6466039013 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -9,19 +9,186 @@
#include "qvaluecombobox.h"
#include <QApplication>
-#include <QDoubleSpinBox>
+#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
-#include <qmath.h> // for qPow()
+#include <QLineEdit>
+
+/** QSpinBox that uses fixed-point numbers internally and uses our own
+ * formatting/parsing functions.
+ */
+class AmountSpinBox: public QAbstractSpinBox
+{
+ Q_OBJECT
+public:
+ explicit AmountSpinBox(QWidget *parent):
+ QAbstractSpinBox(parent),
+ currentUnit(BitcoinUnits::BTC),
+ singleStep(100000) // satoshis
+ {
+ setAlignment(Qt::AlignRight);
+
+ connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
+ }
+
+ QValidator::State validate(QString &text, int &pos) const
+ {
+ if(text.isEmpty())
+ return QValidator::Intermediate;
+ bool valid = false;
+ parse(text, &valid);
+ /* Make sure we return Intermediate so that fixup() is called on defocus */
+ return valid ? QValidator::Intermediate : QValidator::Invalid;
+ }
+
+ void fixup(QString &input) const
+ {
+ bool valid = false;
+ qint64 val = parse(input, &valid);
+ if(valid)
+ {
+ input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
+ lineEdit()->setText(input);
+ }
+ }
+
+ qint64 value(bool *valid_out=0) const
+ {
+ return parse(text(), valid_out);
+ }
+
+ void setValue(qint64 value)
+ {
+ lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
+ emit valueChanged();
+ }
+
+ void stepBy(int steps)
+ {
+ bool valid = false;
+ qint64 val = value(&valid);
+ val = val + steps * singleStep;
+ val = qMin(qMax(val, Q_INT64_C(0)), BitcoinUnits::maxMoney());
+ setValue(val);
+ }
+
+ StepEnabled stepEnabled() const
+ {
+ StepEnabled rv = 0;
+ if(text().isEmpty()) // Allow step-up with empty field
+ return StepUpEnabled;
+ bool valid = false;
+ qint64 val = value(&valid);
+ if(valid)
+ {
+ if(val > 0)
+ rv |= StepDownEnabled;
+ if(val < BitcoinUnits::maxMoney())
+ rv |= StepUpEnabled;
+ }
+ return rv;
+ }
+
+ void setDisplayUnit(int unit)
+ {
+ bool valid = false;
+ qint64 val = value(&valid);
+
+ currentUnit = unit;
+
+ if(valid)
+ setValue(val);
+ else
+ clear();
+ }
+
+ void setSingleStep(qint64 step)
+ {
+ singleStep = step;
+ }
+
+ QSize minimumSizeHint() const
+ {
+ if(cachedMinimumSizeHint.isEmpty())
+ {
+ ensurePolished();
+
+ const QFontMetrics fm(fontMetrics());
+ int h = lineEdit()->minimumSizeHint().height();
+ int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
+ w += 2; // cursor blinking space
+
+ QStyleOptionSpinBox opt;
+ initStyleOption(&opt);
+ QSize hint(w, h);
+ QSize extra(35, 6);
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ // get closer to final result by repeating the calculation
+ opt.rect.setSize(hint + extra);
+ extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
+ QStyle::SC_SpinBoxEditField, this).size();
+ hint += extra;
+
+ opt.rect = rect();
+
+ cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
+ .expandedTo(QApplication::globalStrut());
+ }
+ return cachedMinimumSizeHint;
+ }
+private:
+ int currentUnit;
+ qint64 singleStep;
+ mutable QSize cachedMinimumSizeHint;
+
+ /**
+ * Parse a string into a number of base monetary units and
+ * return validity.
+ * @note Must return 0 if !valid.
+ */
+ qint64 parse(const QString &text, bool *valid_out=0) const
+ {
+ qint64 val = 0;
+ bool valid = BitcoinUnits::parse(currentUnit, text, &val);
+ if(valid)
+ {
+ if(val < 0 || val > BitcoinUnits::maxMoney())
+ valid = false;
+ }
+ if(valid_out)
+ *valid_out = valid;
+ return valid ? val : 0;
+ }
+
+protected:
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
+ {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (keyEvent->key() == Qt::Key_Comma)
+ {
+ // Translate a comma into a period
+ QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
+ return QAbstractSpinBox::event(&periodKeyEvent);
+ }
+ }
+ return QAbstractSpinBox::event(event);
+ }
+
+signals:
+ void valueChanged();
+};
+
+#include "bitcoinamountfield.moc"
BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
QWidget(parent),
- amount(0),
- currentUnit(-1)
+ amount(0)
{
- nSingleStep = 100000; // satoshis
-
- amount = new QDoubleSpinBox(this);
+ amount = new AmountSpinBox(this);
amount->setLocale(QLocale::c());
amount->installEventFilter(this);
amount->setMaximumWidth(170);
@@ -40,21 +207,13 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
- connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged()));
+ connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
// Set default based on configuration
unitChanged(unit->currentIndex());
}
-void BitcoinAmountField::setText(const QString &text)
-{
- if (text.isEmpty())
- amount->clear();
- else
- amount->setValue(text.toDouble());
-}
-
void BitcoinAmountField::clear()
{
amount->clear();
@@ -63,16 +222,9 @@ void BitcoinAmountField::clear()
bool BitcoinAmountField::validate()
{
- bool valid = true;
- if (amount->value() == 0.0)
- valid = false;
- else if (!BitcoinUnits::parse(currentUnit, text(), 0))
- valid = false;
- else if (amount->value() > BitcoinUnits::maxAmount(currentUnit))
- valid = false;
-
+ bool valid = false;
+ value(&valid);
setValid(valid);
-
return valid;
}
@@ -84,14 +236,6 @@ void BitcoinAmountField::setValid(bool valid)
amount->setStyleSheet(STYLE_INVALID);
}
-QString BitcoinAmountField::text() const
-{
- if (amount->text().isEmpty())
- return QString();
- else
- return amount->text();
-}
-
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn)
@@ -99,17 +243,6 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
// Clear invalid flag on focus
setValid(true);
}
- else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
- {
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (keyEvent->key() == Qt::Key_Comma)
- {
- // Translate a comma into a period
- QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
- QApplication::sendEvent(object, &periodKeyEvent);
- return true;
- }
- }
return QWidget::eventFilter(object, event);
}
@@ -122,18 +255,12 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
qint64 BitcoinAmountField::value(bool *valid_out) const
{
- qint64 val_out = 0;
- bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
- if (valid_out)
- {
- *valid_out = valid;
- }
- return val_out;
+ return amount->value(valid_out);
}
void BitcoinAmountField::setValue(qint64 value)
{
- setText(BitcoinUnits::format(currentUnit, value));
+ amount->setValue(value);
}
void BitcoinAmountField::setReadOnly(bool fReadOnly)
@@ -150,28 +277,7 @@ void BitcoinAmountField::unitChanged(int idx)
// Determine new unit ID
int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
- // Parse current value and convert to new unit
- bool valid = false;
- qint64 currentValue = value(&valid);
-
- currentUnit = newUnit;
-
- // Set max length after retrieving the value, to prevent truncation
- amount->setDecimals(BitcoinUnits::decimals(currentUnit));
- amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
- amount->setSingleStep((double)nSingleStep / (double)BitcoinUnits::factor(currentUnit));
-
- if (valid)
- {
- // If value was valid, re-place it in the widget with the new unit
- setValue(currentValue);
- }
- else
- {
- // If current value is invalid, just clear field
- setText("");
- }
- setValid(true);
+ amount->setDisplayUnit(newUnit);
}
void BitcoinAmountField::setDisplayUnit(int newUnit)
@@ -181,6 +287,5 @@ void BitcoinAmountField::setDisplayUnit(int newUnit)
void BitcoinAmountField::setSingleStep(qint64 step)
{
- nSingleStep = step;
- unitChanged(unit->currentIndex());
+ amount->setSingleStep(step);
}
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index 521a9ed561..c713f5d687 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -8,17 +8,18 @@
#include <QWidget>
QT_BEGIN_NAMESPACE
-class QDoubleSpinBox;
class QValueComboBox;
QT_END_NAMESPACE
+class AmountSpinBox;
+
/** Widget for entering bitcoin amounts.
*/
class BitcoinAmountField: public QWidget
{
Q_OBJECT
- Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true)
+ Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY valueChanged USER true)
public:
explicit BitcoinAmountField(QWidget *parent = 0);
@@ -49,20 +50,15 @@ public:
QWidget *setupTabChain(QWidget *prev);
signals:
- void textChanged();
+ void valueChanged();
protected:
/** Intercept focus-in event and ',' key presses */
bool eventFilter(QObject *object, QEvent *event);
private:
- QDoubleSpinBox *amount;
+ AmountSpinBox *amount;
QValueComboBox *unit;
- int currentUnit;
- qint64 nSingleStep;
-
- void setText(const QString &text);
- QString text() const;
private slots:
void unitChanged(int idx);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6b3aa2a2df..5fc2f500b5 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -159,10 +159,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
labelEncryptionIcon = new QLabel();
labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel();
- frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(unitDisplayControl);
- frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(labelEncryptionIcon);
+ if(enableWallet)
+ {
+ frameBlocksLayout->addStretch();
+ frameBlocksLayout->addWidget(unitDisplayControl);
+ frameBlocksLayout->addStretch();
+ frameBlocksLayout->addWidget(labelEncryptionIcon);
+ }
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon);
frameBlocksLayout->addStretch();
@@ -780,11 +783,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
buttons = QMessageBox::Ok;
- // Ensure we get users attention, but only if main window is visible
- // as we don't want to pop up the main window for messages that happen before
- // initialization is finished.
- if(!(style & CClientUIInterface::NOSHOWGUI))
- showNormalIfMinimized();
+ showNormalIfMinimized();
QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this);
int r = mBox.exec();
if (ret != NULL)
@@ -921,6 +920,8 @@ void BitcoinGUI::setEncryptionStatus(int status)
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
{
+ if(!clientModel)
+ return;
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (isHidden())
{
@@ -1012,7 +1013,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
}
-/** So that it responds to left-button clicks */
+/** So that it responds to button clicks */
void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
{
onDisplayUnitsClicked(event->pos());
@@ -1029,10 +1030,6 @@ void UnitDisplayStatusBarControl::createContextMenu()
menu->addAction(menuAction);
}
connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*)));
-
- // what happens on right click.
- setContextMenuPolicy(Qt::CustomContextMenu);
- connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&)));
}
/** Lets the control know about the Options Model (and its signals) */
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index bbc9b2e5af..6f506d3f25 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -4,6 +4,8 @@
#include "bitcoinunits.h"
+#include "core.h"
+
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
@@ -61,8 +63,8 @@ QString BitcoinUnits::description(int unit)
switch(unit)
{
case BTC: return QString("Bitcoins");
- case mBTC: return QString("Milli-Bitcoins (1 / 1,000)");
- case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)");
+ case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
+ case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
default: return QString("???");
}
}
@@ -78,28 +80,6 @@ qint64 BitcoinUnits::factor(int unit)
}
}
-qint64 BitcoinUnits::maxAmount(int unit)
-{
- switch(unit)
- {
- case BTC: return Q_INT64_C(21000000);
- case mBTC: return Q_INT64_C(21000000000);
- case uBTC: return Q_INT64_C(21000000000000);
- default: return 0;
- }
-}
-
-int BitcoinUnits::amountDigits(int unit)
-{
- switch(unit)
- {
- case BTC: return 8; // 21,000,000 (# digits, without commas)
- case mBTC: return 11; // 21,000,000,000
- case uBTC: return 14; // 21,000,000,000,000
- default: return 0;
- }
-}
-
int BitcoinUnits::decimals(int unit)
{
switch(unit)
@@ -111,7 +91,7 @@ int BitcoinUnits::decimals(int unit)
}
}
-QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
+QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle separators)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
@@ -125,11 +105,20 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
QString quotient_str = QString::number(quotient);
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
- // Right-trim excess zeros after the decimal point
- int nTrim = 0;
- for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i)
- ++nTrim;
- remainder_str.chop(nTrim);
+ // Use SI-stule separators as these are locale indendent and can't be
+ // confused with the decimal marker. Rule is to use a thin space every
+ // three digits on *both* sides of the decimal point - but only if there
+ // are five or more digits
+ QChar thin_sp(THIN_SP_CP);
+ int q_size = quotient_str.size();
+ if (separators == separatorAlways || (separators == separatorStandard && q_size > 4))
+ for (int i = 3; i < q_size; i += 3)
+ quotient_str.insert(q_size - i, thin_sp);
+
+ int r_size = remainder_str.size();
+ if (separators == separatorAlways || (separators == separatorStandard && r_size > 4))
+ for (int i = 3, adj = 0; i < r_size ; i += 3, adj++)
+ remainder_str.insert(i + adj, thin_sp);
if (n < 0)
quotient_str.insert(0, '-');
@@ -138,17 +127,43 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
return quotient_str + QString(".") + remainder_str;
}
-QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign)
+
+// TODO: Review all remaining calls to BitcoinUnits::formatWithUnit to
+// TODO: determine whether the output is used in a plain text context
+// TODO: or an HTML context (and replace with
+// TODO: BtcoinUnits::formatHtmlWithUnit in the latter case). Hopefully
+// TODO: there aren't instances where the result could be used in
+// TODO: either context.
+
+// NOTE: Using formatWithUnit in an HTML context risks wrapping
+// quantities at the thousands separator. More subtly, it also results
+// in a standard space rather than a thin space, due to a bug in Qt's
+// XML whitespace canonicalisation
+//
+// Please take care to use formatHtmlWithUnit instead, when
+// appropriate.
+
+QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators)
+{
+ return format(unit, amount, plussign, separators) + QString(" ") + name(unit);
+}
+
+QString BitcoinUnits::formatHtmlWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators)
{
- return format(unit, amount, plussign) + QString(" ") + name(unit);
+ QString str(formatWithUnit(unit, amount, plussign, separators));
+ str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
+ return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
+
bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out)
{
if(!valid(unit) || value.isEmpty())
return false; // Refuse to parse invalid unit or empty string
int num_decimals = decimals(unit);
- QStringList parts = value.split(".");
+
+ // Ignore spaces and thin spaces when parsing
+ QStringList parts = removeSpaces(value).split(".");
if(parts.size() > 2)
{
@@ -215,3 +230,8 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
}
return QVariant();
}
+
+qint64 BitcoinUnits::maxMoney()
+{
+ return MAX_MONEY;
+}
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index da34ed8976..be9dca6012 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -8,6 +8,37 @@
#include <QAbstractListModel>
#include <QString>
+// U+2009 THIN SPACE = UTF-8 E2 80 89
+#define REAL_THIN_SP_CP 0x2009
+#define REAL_THIN_SP_UTF8 "\xE2\x80\x89"
+#define REAL_THIN_SP_HTML "&thinsp;"
+
+// U+200A HAIR SPACE = UTF-8 E2 80 8A
+#define HAIR_SP_CP 0x200A
+#define HAIR_SP_UTF8 "\xE2\x80\x8A"
+#define HAIR_SP_HTML "&#8202;"
+
+// U+2006 SIX-PER-EM SPACE = UTF-8 E2 80 86
+#define SIXPEREM_SP_CP 0x2006
+#define SIXPEREM_SP_UTF8 "\xE2\x80\x86"
+#define SIXPEREM_SP_HTML "&#8198;"
+
+// U+2007 FIGURE SPACE = UTF-8 E2 80 87
+#define FIGURE_SP_CP 0x2007
+#define FIGURE_SP_UTF8 "\xE2\x80\x87"
+#define FIGURE_SP_HTML "&#8199;"
+
+// QMessageBox seems to have a bug whereby it doesn't display thin/hair spaces
+// correctly. Workaround is to display a space in a small font. If you
+// change this, please test that it doesn't cause the parent span to start
+// wrapping.
+#define HTML_HACK_SP "<span style='white-space: nowrap; font-size: 6pt'> </span>"
+
+// Define THIN_SP_* variables to be our preferred type of thin space
+#define THIN_SP_CP REAL_THIN_SP_CP
+#define THIN_SP_UTF8 REAL_THIN_SP_UTF8
+#define THIN_SP_HTML HTML_HACK_SP
+
/** Bitcoin unit definitions. Encapsulates parsing and formatting
and serves as list model for drop-down selection boxes.
*/
@@ -28,6 +59,13 @@ public:
uBTC
};
+ enum SeparatorStyle
+ {
+ separatorNever,
+ separatorStandard,
+ separatorAlways
+ };
+
//! @name Static API
//! Unit conversion and formatting
///@{
@@ -44,16 +82,13 @@ public:
static QString description(int unit);
//! Number of Satoshis (1e-8) per unit
static qint64 factor(int unit);
- //! Max amount per unit
- static qint64 maxAmount(int unit);
- //! Number of amount digits (to represent max number of coins)
- static int amountDigits(int unit);
//! Number of decimals left
static int decimals(int unit);
//! Format as string
- static QString format(int unit, qint64 amount, bool plussign=false);
+ static QString format(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Format as string (with unit)
- static QString formatWithUnit(int unit, qint64 amount, bool plussign=false);
+ static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString formatHtmlWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, qint64 *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
@@ -71,6 +106,19 @@ public:
QVariant data(const QModelIndex &index, int role) const;
///@}
+ static QString removeSpaces(QString text)
+ {
+ text.remove(' ');
+ text.remove(QChar(THIN_SP_CP));
+#if (THIN_SP_CP != REAL_THIN_SP_CP)
+ text.remove(QChar(REAL_THIN_SP_CP));
+#endif
+ return text;
+ }
+
+ //! Return maximum number of base units (Satoshis)
+ static qint64 maxMoney();
+
private:
QList<BitcoinUnits::Unit> unitlist;
};
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index b4ddda3eaa..7b30f8de09 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -225,7 +225,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
// context menu action: copy amount
void CoinControlDialog::copyAmount()
{
- GUIUtil::setClipboard(contextMenuItem->text(COLUMN_AMOUNT));
+ GUIUtil::setClipboard(BitcoinUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT)));
}
// context menu action: copy label
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 696761e234..5ae4bc833d 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -23,10 +23,6 @@ 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 60a131df7e..33a50a078d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -187,7 +187,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
if (info.amount)
{
- ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount));
+ ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever));
paramCount++;
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 597be40abd..0117d2e633 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -15,11 +15,11 @@
#include "optionsmodel.h"
#include "main.h" // for MAX_SCRIPTCHECK_THREADS
+#include "netbase.h"
+#include "txdb.h" // for -dbcache defaults
#ifdef ENABLE_WALLET
#include "wallet.h" // for CWallet::minTxFee
#endif
-#include "netbase.h"
-#include "txdb.h" // for -dbcache defaults
#include <QDir>
#include <QIntValidator>
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 89c2ec7453..9699f6eea6 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -32,7 +32,6 @@ public:
ProxyUse, // bool
ProxyIP, // QString
ProxyPort, // int
- ProxySocksVersion, // int
Fee, // qint64
DisplayUnit, // BitcoinUnits::Unit
DisplayAddresses, // bool
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 1278f368cf..1c700b37ff 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -72,7 +72,7 @@ public:
foreground = option.palette.color(QPalette::Text);
}
painter->setPen(foreground);
- QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true);
+ QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways);
if(!confirmed)
{
amountText = QString("[") + amountText + QString("]");
@@ -147,14 +147,14 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64
currentWatchOnlyBalance = watchOnlyBalance;
currentWatchUnconfBalance = watchUnconfBalance;
currentWatchImmatureBalance = watchImmatureBalance;
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance));
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways));
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index b5a998f9f5..9e3976644e 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -79,10 +79,17 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons
case Amount:
if (rec->recipient.amount == 0 && role == Qt::DisplayRole)
return tr("(no amount)");
+ else if (role == Qt::EditRole)
+ return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::separatorNever);
else
return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount);
}
}
+ else if (role == Qt::TextAlignmentRole)
+ {
+ if (index.column() == Amount)
+ return (int)(Qt::AlignRight|Qt::AlignVCenter);
+ }
return QVariant();
}
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index e1f40ddd09..9b67f8125f 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -473,6 +473,10 @@ void RPCConsole::on_tabWidget_currentChanged(int index)
{
ui->lineEdit->setFocus();
}
+ else if(ui->tabWidget->widget(index) == ui->tab_peers)
+ {
+ initPeerTable();
+ }
}
void RPCConsole::on_openDebugLogfileButton_clicked()
@@ -648,11 +652,27 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats)
ui->peerBanScore->setText(tr("Fetching..."));
}
+void RPCConsole::initPeerTable()
+{
+ if (!clientModel)
+ return;
+
+ // peerWidget needs a resize in case the dialog has non-default geometry
+ columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
+
+ // start PeerTableModel auto refresh
+ clientModel->getPeerTableModel()->startAutoRefresh(1000);
+}
+
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void RPCConsole::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
+
+ if (!clientModel)
+ return;
+
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
}
@@ -660,17 +680,16 @@ void RPCConsole::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
- // peerWidget needs a resize in case the dialog has non-default geometry
- columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
-
- // start PeerTableModel auto refresh
- clientModel->getPeerTableModel()->startAutoRefresh(1000);
+ initPeerTable();
}
void RPCConsole::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
+ if (!clientModel)
+ return;
+
// stop PeerTableModel auto refresh
clientModel->getPeerTableModel()->stopAutoRefresh();
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 3aeff3eace..94672b30cc 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -47,6 +47,8 @@ protected:
private:
/** show detailed information on ui about selected node */
void updateNodeDetail(const CNodeCombinedStats *combinedStats);
+ /** initialize peer table */
+ void initPeerTable();
enum ColumnWidths
{
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6f10ed5b0b..25e3d2a0dc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -143,7 +143,7 @@ void SendCoinsDialog::on_sendButton_clicked()
foreach(const SendCoinsRecipient &rcp, recipients)
{
// generate bold amount string
- QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
+ QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
amount.append("</b>");
// generate monospace address string
QString address = "<span style='font-family: monospace;'>" + rcp.address;
@@ -211,7 +211,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
// append fee string if a fee is required
questionString.append("<hr /><span style='color:#aa0000;'>");
- questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
+ questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> ");
questionString.append(tr("added as transaction fee"));
}
@@ -223,10 +223,10 @@ void SendCoinsDialog::on_sendButton_clicked()
foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
{
if(u != model->getOptionsModel()->getDisplayUnit())
- alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount));
+ alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
}
questionString.append(tr("Total Amount %1 (= %2)")
- .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
+ .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
.arg(alternativeUnits.join(" " + tr("or") + " ")));
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index e0f56f8cd2..52545c3857 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -34,6 +34,12 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
GUIUtil::setupAddressWidget(ui->payTo, this);
// just a label for displaying bitcoin address(es)
ui->payTo_is->setFont(GUIUtil::bitcoinAddressFont());
+
+ // Connect signals
+ connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged()));
+ connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked()));
+ connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
}
SendCoinsEntry::~SendCoinsEntry()
@@ -72,11 +78,6 @@ void SendCoinsEntry::setModel(WalletModel *model)
if (model && model->getOptionsModel())
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
- connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged()));
- connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
- connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked()));
- connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked()));
-
clear();
}
@@ -130,6 +131,13 @@ bool SendCoinsEntry::validate()
retval = false;
}
+ // Sending a zero amount is invalid
+ if (ui->payAmount->value(0) <= 0)
+ {
+ ui->payAmount->setValid(false);
+ retval = false;
+ }
+
// Reject dust outputs:
if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value())) {
ui->payAmount->setValid(false);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 36e4d50e30..4f6e3169f5 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -11,11 +11,11 @@
#include "db.h"
#include "main.h"
#include "paymentserver.h"
+#include "script.h"
#include "transactionrecord.h"
#include "timedata.h"
#include "ui_interface.h"
#include "wallet.h"
-#include "script.h"
#include <stdint.h>
#include <string>
@@ -136,7 +136,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
- strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
+ strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
else
strHTML += "(" + tr("not accepted") + ")";
strHTML += "<br>";
@@ -146,7 +146,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Credit
//
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>";
}
else
{
@@ -197,9 +197,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
}
}
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
if(toSelf)
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
}
if (fAllToMe)
@@ -207,13 +207,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Payment to self
int64_t nChange = wtx.GetChange();
int64_t nValue = nCredit - nChange;
- strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
- strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
+ strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
+ strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
}
int64_t nTxFee = nDebit - wtx.GetValueOut();
if (nTxFee > 0)
- strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>";
+ strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
}
else
{
@@ -222,14 +222,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if (wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
}
}
- strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "<br>";
+ strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>";
//
// Message
@@ -275,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if(wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
@@ -304,7 +304,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
}
- strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue);
+ strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
}
diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp
index 7293029787..f9546fddb5 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(false)
+ showInactive(true)
{
}
@@ -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 && type == TransactionRecord::Other)
+ if(!showInactive && status == TransactionStatus::Conflicted)
return false;
if(!(TYPE(type) & typeFilter))
return false;
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 7d29c212b3..d7bd25e08b 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -184,8 +184,6 @@ 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)
@@ -229,7 +227,6 @@ 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)
{
@@ -238,7 +235,6 @@ 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)
{
@@ -249,13 +245,13 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.status = TransactionStatus::Confirmed;
}
}
+
}
-bool TransactionRecord::statusUpdateNeeded(int64_t nConflictsReceived)
+bool TransactionRecord::statusUpdateNeeded()
{
AssertLockHeld(cs_main);
- return (status.cur_num_blocks != chainActive.Height() ||
- status.cur_num_conflicts != nConflictsReceived);
+ return status.cur_num_blocks != chainActive.Height();
}
QString TransactionRecord::getTxID() const
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index d3cfa77d97..626b7654c6 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -19,17 +19,9 @@ class TransactionStatus
{
public:
TransactionStatus():
- countsForBalance(false),
- sortKey(""),
- matures_in(0),
- status(Offline),
- hasConflicting(false),
- depth(0),
- open_for(0),
- cur_num_blocks(-1),
- cur_num_conflicts(-1)
- {
- }
+ countsForBalance(false), sortKey(""),
+ matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1)
+ { }
enum Status {
Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/
@@ -59,10 +51,6 @@ 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
@@ -71,10 +59,6 @@ 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
@@ -152,7 +136,7 @@ public:
/** Return whether a status update is needed.
*/
- bool statusUpdateNeeded(int64_t nConflictsReceived);
+ bool statusUpdateNeeded();
};
#endif // TRANSACTIONRECORD_H
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 3605cc1bad..7acb0e8871 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -5,7 +5,6 @@
#include "transactiontablemodel.h"
#include "addresstablemodel.h"
-#include "bitcoinunits.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "optionsmodel.h"
@@ -168,7 +167,8 @@ public:
parent->endRemoveRows();
break;
case CT_UPDATED:
- emit parent->dataChanged(parent->index(lowerIndex, parent->Status), parent->index(upperIndex-1, parent->Amount));
+ // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
+ // visible transactions.
break;
}
}
@@ -189,21 +189,20 @@ 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 or conflicts came in since last check),
- // update the status of this transaction from the wallet. Otherwise,
+ // If a status update is needed (blocks 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(wallet->nConflictsReceived))
+ if(lockWallet && rec->statusUpdateNeeded())
{
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;
}
}
}
@@ -369,8 +368,6 @@ 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();
}
@@ -436,9 +433,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
return QVariant();
}
-QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const
+QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const
{
- QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit);
+ QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators);
if(showUnconfirmed)
{
if(!wtx->status.countsForBalance)
@@ -523,7 +520,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ToAddress:
return formatTxToAddress(rec, false);
case Amount:
- return formatTxAmount(rec);
+ return formatTxAmount(rec, true, BitcoinUnits::separatorAlways);
}
break;
case Qt::EditRole:
@@ -546,13 +543,7 @@ 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)
{
@@ -586,7 +577,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ConfirmedRole:
return rec->status.countsForBalance;
case FormattedAmountRole:
- return formatTxAmount(rec, false);
+ // Used for copy/export, so don't include separators
+ return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
case StatusRole:
return rec->status.status;
}
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index e8b6ed065d..2124d3dd1c 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -5,6 +5,8 @@
#ifndef TRANSACTIONTABLEMODEL_H
#define TRANSACTIONTABLEMODEL_H
+#include "bitcoinunits.h"
+
#include <QAbstractTableModel>
#include <QStringList>
@@ -78,7 +80,7 @@ private:
QString formatTxDate(const TransactionRecord *wtx) const;
QString formatTxType(const TransactionRecord *wtx) const;
QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const;
- QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const;
+ QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const;
QString formatTooltip(const TransactionRecord *rec) const;
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
QVariant txAddressDecoration(const TransactionRecord *wtx) const;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index d6d210a561..7e8b71d8ea 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -123,6 +123,8 @@ TransactionView::TransactionView(QWidget *parent) :
view->setTabKeyNavigation(false);
view->setContextMenuPolicy(Qt::CustomContextMenu);
+ view->installEventFilter(this);
+
transactionView = view;
// Actions
@@ -480,3 +482,22 @@ void TransactionView::resizeEvent(QResizeEvent* event)
QWidget::resizeEvent(event);
columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress);
}
+
+// Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text
+bool TransactionView::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->key() == Qt::Key_C && ke->modifiers().testFlag(Qt::ControlModifier))
+ {
+ QModelIndex i = this->transactionView->currentIndex();
+ if (i.isValid() && i.column() == TransactionTableModel::Amount)
+ {
+ GUIUtil::setClipboard(i.data(TransactionTableModel::FormattedAmountRole).toString());
+ return true;
+ }
+ }
+ }
+ return QWidget::eventFilter(obj, event);
+}
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 7a89fa11ca..618efbc565 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -8,6 +8,7 @@
#include "guiutil.h"
#include <QWidget>
+#include <QKeyEvent>
class TransactionFilterProxy;
class WalletModel;
@@ -78,6 +79,8 @@ private:
virtual void resizeEvent(QResizeEvent* event);
+ bool eventFilter(QObject *obj, QEvent *event);
+
private slots:
void contextualMenu(const QPoint &);
void dateRangeChanged();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 7317c32766..0ad123f39d 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -35,6 +35,8 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
+ fProcessingQueuedTransactions = false;
+
addressTableModel = new AddressTableModel(wallet, this);
transactionTableModel = new TransactionTableModel(wallet, this);
recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
@@ -162,14 +164,6 @@ 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);
@@ -492,8 +486,15 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int
if (nProgress == 100)
{
fQueueNotifications = false;
- BOOST_FOREACH(const PAIRTYPE(uint256, ChangeType)& notification, vQueueNotifications)
- NotifyTransactionChanged(walletmodel, NULL, notification.first, notification.second);
+ if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
+ QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
+ for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
+ {
+ if (vQueueNotifications.size() - i <= 10)
+ QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
+
+ NotifyTransactionChanged(walletmodel, NULL, vQueueNotifications[i].first, vQueueNotifications[i].second);
+ }
std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear
}
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 7ad54ff8e6..2bb91d85a9 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -133,6 +133,7 @@ public:
qint64 getWatchImmatureBalance() const;
int getNumTransactions() const;
EncryptionStatus getEncryptionStatus() const;
+ bool processingQueuedTransactions() { return fProcessingQueuedTransactions; }
// Check address for validity
bool validateAddress(const QString &address);
@@ -196,6 +197,7 @@ public:
private:
CWallet *wallet;
+ bool fProcessingQueuedTransactions;
// Wallet has an options model for wallet-specific options
// (transaction fee, for example)
@@ -256,6 +258,8 @@ public slots:
void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
/* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */
void pollBalanceChanged();
+ /* Needed to update fProcessingQueuedTransactions through a QueuedConnection */
+ void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; }
};
#endif // WALLETMODEL_H
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 1cef48344f..b40ddc0a2f 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -137,7 +137,7 @@ void WalletView::setWalletModel(WalletModel *walletModel)
void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
// Prevent balloon-spam when initial block download is in progress
- if (!walletModel || !clientModel || clientModel->inInitialBlockDownload())
+ if (!walletModel || walletModel->processingQueuedTransactions() || !clientModel || clientModel->inInitialBlockDownload())
return;
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
diff --git a/src/random.cpp b/src/random.cpp
new file mode 100644
index 0000000000..0d20d205ac
--- /dev/null
+++ b/src/random.cpp
@@ -0,0 +1,139 @@
+// 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 "random.h"
+
+#ifdef WIN32
+#include "compat.h" // for Windows API
+#endif
+#include "util.h" // for LogPrint()
+
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+static inline int64_t GetPerformanceCounter()
+{
+ int64_t nCounter = 0;
+#ifdef WIN32
+ QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
+#else
+ timeval t;
+ gettimeofday(&t, NULL);
+ nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec);
+#endif
+ return nCounter;
+}
+
+void RandAddSeed()
+{
+ // Seed with CPU performance counter
+ int64_t nCounter = GetPerformanceCounter();
+ RAND_add(&nCounter, sizeof(nCounter), 1.5);
+ OPENSSL_cleanse((void*)&nCounter, sizeof(nCounter));
+}
+
+void RandAddSeedPerfmon()
+{
+ RandAddSeed();
+
+ // This can take up to 2 seconds, so only do it every 10 minutes
+ static int64_t nLastPerfmon;
+ if (GetTime() < nLastPerfmon + 10 * 60)
+ return;
+ nLastPerfmon = GetTime();
+
+#ifdef WIN32
+ // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
+ // Seed with the entire set of perfmon data
+ std::vector <unsigned char> vData(250000,0);
+ long ret = 0;
+ unsigned long nSize = 0;
+ const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
+ while (true)
+ {
+ nSize = vData.size();
+ ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize);
+ if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
+ break;
+ vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially
+ }
+ RegCloseKey(HKEY_PERFORMANCE_DATA);
+ if (ret == ERROR_SUCCESS)
+ {
+ RAND_add(begin_ptr(vData), nSize, nSize/100.0);
+ OPENSSL_cleanse(begin_ptr(vData), nSize);
+ LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
+ } else {
+ static bool warned = false; // Warn only once
+ if (!warned)
+ {
+ LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
+ warned = true;
+ }
+ }
+#endif
+}
+
+bool GetRandBytes(unsigned char *buf, int num)
+{
+ if (RAND_bytes(buf, num) != 1) {
+ LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
+ return false;
+ }
+ return true;
+}
+
+uint64_t GetRand(uint64_t nMax)
+{
+ if (nMax == 0)
+ return 0;
+
+ // The range of the random source must be a multiple of the modulus
+ // to give every possible output value an equal possibility
+ uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax;
+ uint64_t nRand = 0;
+ do {
+ GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
+ } while (nRand >= nRange);
+ return (nRand % nMax);
+}
+
+int GetRandInt(int nMax)
+{
+ return GetRand(nMax);
+}
+
+uint256 GetRandHash()
+{
+ uint256 hash;
+ GetRandBytes((unsigned char*)&hash, sizeof(hash));
+ return hash;
+}
+
+uint32_t insecure_rand_Rz = 11;
+uint32_t insecure_rand_Rw = 11;
+void seed_insecure_rand(bool fDeterministic)
+{
+ // The seed values have some unlikely fixed points which we avoid.
+ if(fDeterministic)
+ {
+ insecure_rand_Rz = insecure_rand_Rw = 11;
+ } else {
+ uint32_t tmp;
+ do {
+ GetRandBytes((unsigned char*)&tmp, 4);
+ } while(tmp == 0 || tmp == 0x9068ffffU);
+ insecure_rand_Rz = tmp;
+ do {
+ GetRandBytes((unsigned char*)&tmp, 4);
+ } while(tmp == 0 || tmp == 0x464fffffU);
+ insecure_rand_Rw = tmp;
+ }
+}
diff --git a/src/random.h b/src/random.h
new file mode 100644
index 0000000000..a599b08478
--- /dev/null
+++ b/src/random.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_RANDOM_H
+#define BITCOIN_RANDOM_H
+
+#include "uint256.h"
+
+#include <stdint.h>
+
+/**
+ * Seed OpenSSL PRNG with additional entropy data
+ */
+void RandAddSeed();
+void RandAddSeedPerfmon();
+
+/**
+ * Functions to gather random data via the OpenSSL PRNG
+ */
+bool GetRandBytes(unsigned char *buf, int num);
+uint64_t GetRand(uint64_t nMax);
+int GetRandInt(int nMax);
+uint256 GetRandHash();
+
+/**
+ * Seed insecure_rand using the random pool.
+ * @param Deterministic Use a determinstic seed
+ */
+void seed_insecure_rand(bool fDeterministic = false);
+
+/**
+ * MWC RNG of George Marsaglia
+ * This is intended to be fast. It has a period of 2^59.3, though the
+ * least significant 16 bits only have a period of about 2^30.1.
+ *
+ * @return random value
+ */
+extern uint32_t insecure_rand_Rz;
+extern uint32_t insecure_rand_Rw;
+static inline uint32_t insecure_rand(void)
+{
+ insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16);
+ insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16);
+ return (insecure_rand_Rw << 16) + insecure_rand_Rz;
+}
+
+#endif // BITCOIN_RANDOM_H
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 5edeecf933..a0921453cc 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -85,6 +85,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getrawmempool", 0 },
{ "estimatefee", 0 },
{ "estimatepriority", 0 },
+ { "prioritisetransaction", 1 },
+ { "prioritisetransaction", 2 },
};
class CRPCConvertTable
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 4193f41b49..ff2361482b 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -72,16 +72,17 @@ Value importprivkey(const Array& params, bool fHelp)
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
"\nArguments:\n"
"1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
- "2. \"label\" (string, optional) an optional label\n"
+ "2. \"label\" (string, optional, default=\"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ "\nNote: This call can take minutes to complete if rescan is true.\n"
"\nExamples:\n"
"\nDump a private key\n"
+ HelpExampleCli("dumpprivkey", "\"myaddress\"") +
- "\nImport the private key\n"
+ "\nImport the private key with rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\"") +
- "\nImport using a label\n"
+ "\nImport using a label and without rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
);
@@ -137,8 +138,21 @@ Value importaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
- "importaddress <address> [label] [rescan=true]\n"
- "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.");
+ "importaddress \"address\" ( \"label\" rescan )\n"
+ "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
+ "\nArguments:\n"
+ "1. \"address\" (string, required) The address\n"
+ "2. \"label\" (string, optional, default=\"\") An optional label\n"
+ "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
+ "\nNote: This call can take minutes to complete if rescan is true.\n"
+ "\nExamples:\n"
+ "\nImport an address with rescan\n"
+ + HelpExampleCli("importaddress", "\"myaddress\"") +
+ "\nImport using a label without rescan\n"
+ + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
+ );
CScript script;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index c7621dc137..cbb4ab2f8b 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -252,11 +252,30 @@ Value prioritisetransaction(const Array& params, bool fHelp)
if (fHelp || params.size() != 3)
throw runtime_error(
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
- "Accepts the transaction into mined blocks at a higher (or lower) priority");
+ "Accepts the transaction into mined blocks at a higher (or lower) priority\n"
+ "\nArguments:\n"
+ "1. \"txid\" (string, required) The transaction id.\n"
+ "2. priority delta (numeric, required) The priority to add or subtract.\n"
+ " The transaction selection algorithm considers the tx as it would have a higher priority.\n"
+ " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n"
+ "3. fee delta (numeric, required) The absolute fee value to add or subtract in bitcoin.\n"
+ " The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
+ " considers the transaction as it would have paid a higher (or lower) fee.\n"
+ "\nResult\n"
+ "true (boolean) Returns true\n"
+ "\nExamples:\n"
+ + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 0.00010000")
+ + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 0.00010000")
+ );
uint256 hash;
hash.SetHex(params[0].get_str());
- mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), params[2].get_int64());
+
+ int64_t nAmount = 0;
+ if (params[2].get_real() != 0.0)
+ nAmount = AmountFromValue(params[2]);
+
+ mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
return true;
}
@@ -324,6 +343,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
);
std::string strMode = "template";
+ Value lpval = Value::null;
if (params.size() > 0)
{
const Object& oparam = params[0].get_obj();
@@ -336,6 +356,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
}
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
+ lpval = find_value(oparam, "longpollid");
}
if (strMode != "template")
@@ -347,8 +368,63 @@ Value getblocktemplate(const Array& params, bool fHelp)
if (IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
- // Update block
static unsigned int nTransactionsUpdatedLast;
+
+ if (lpval.type() != null_type)
+ {
+ // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
+ uint256 hashWatchedChain;
+ boost::system_time checktxtime;
+ unsigned int nTransactionsUpdatedLastLP;
+
+ if (lpval.type() == str_type)
+ {
+ // Format: <hashBestChain><nTransactionsUpdatedLast>
+ std::string lpstr = lpval.get_str();
+
+ hashWatchedChain.SetHex(lpstr.substr(0, 64));
+ nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
+ }
+ else
+ {
+ // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
+ hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
+ }
+
+ // Release the wallet and main lock while waiting
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+ LEAVE_CRITICAL_SECTION(cs_main);
+ {
+ checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
+
+ boost::unique_lock<boost::mutex> lock(csBestBlock);
+ while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
+ {
+ if (!cvBlockChange.timed_wait(lock, checktxtime))
+ {
+ // Timeout: Check transactions for update
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
+ break;
+ checktxtime += boost::posix_time::seconds(10);
+ }
+ }
+ }
+ ENTER_CRITICAL_SECTION(cs_main);
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+
+ if (!IsRPCRunning())
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
+ // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
+ }
+
+ // Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
static CBlockTemplate* pblocktemplate;
@@ -436,6 +512,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable));
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index cff795bdf4..bd992397b8 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -27,6 +27,19 @@ using namespace boost::assign;
using namespace json_spirit;
using namespace std;
+/**
+ * @note Do not add or change anything in the information returned by this
+ * method. `getinfo` exists for backwards-compatibilty only. It combines
+ * information from wildly different sources in the program, which is a mess,
+ * and is thus planned to be deprecated eventually.
+ *
+ * Based on the source of the information, new information should be added to:
+ * - `getblockchaininfo`,
+ * - `getnetworkinfo` or
+ * - `getwalletinfo`
+ *
+ * Or alternatively, create a specific query method for the information.
+ **/
Value getinfo(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index cf2c293caf..88e7c4ab07 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -79,6 +79,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
"\nbResult:\n"
"[\n"
" {\n"
+ " \"id\": n, (numeric) Peer index\n"
" \"addr\":\"host:port\", (string) The ip address and port of the peer\n"
" \"addrlocal\":\"ip:port\", (string) local address\n"
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
@@ -97,8 +98,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
" \"syncnode\" : true|false (booleamn) if sync node\n"
" }\n"
" ,...\n"
- "}\n"
-
+ "]\n"
"\nExamples:\n"
+ HelpExampleCli("getpeerinfo", "")
+ HelpExampleRpc("getpeerinfo", "")
@@ -113,6 +113,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
Object obj;
CNodeStateStats statestats;
bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
+ obj.push_back(Pair("id", stats.nodeid));
obj.push_back(Pair("addr", stats.addrName));
if (!(stats.addrLocal.empty()))
obj.push_back(Pair("addrlocal", stats.addrLocal));
@@ -137,6 +138,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("syncheight", statestats.nSyncHeight));
}
obj.push_back(Pair("syncnode", stats.fSyncNode));
+ obj.push_back(Pair("whitelisted", stats.fWhitelisted));
ret.push_back(obj);
}
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 2306b1b883..1efe38e830 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -523,7 +523,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
- " \"redeemScript\": \"hex\" (string, required) redeem script\n"
+ " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
" }\n"
" ,...\n"
" ]\n"
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 48b87f5139..5deb6a4e08 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -32,6 +32,7 @@ using namespace std;
static std::string strRPCUserColonPass;
+static bool fRPCRunning = false;
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio::io_service* rpc_io_service = NULL;
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
@@ -531,7 +532,7 @@ void StartRPCThreads()
(mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())
{
unsigned char rand_pwd[32];
- RAND_bytes(rand_pwd, 32);
+ GetRandBytes(rand_pwd, 32);
string strWhatAmI = "To use bitcoind";
if (mapArgs.count("-server"))
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
@@ -659,6 +660,7 @@ void StartRPCThreads()
rpc_worker_group = new boost::thread_group();
for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
void StartDummyRPCThread()
@@ -671,12 +673,15 @@ void StartDummyRPCThread()
rpc_dummy_work = new asio::io_service::work(*rpc_io_service);
rpc_worker_group = new boost::thread_group();
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
}
void StopRPCThreads()
{
if (rpc_io_service == NULL) return;
+ // Set this to false first, so that longpolling loops will exit when woken up
+ fRPCRunning = false;
// First, cancel all timers and acceptors
// This is not done automatically by ->stop(), and in some cases the destructor of
@@ -698,6 +703,7 @@ void StopRPCThreads()
deadlineTimers.clear();
rpc_io_service->stop();
+ cvBlockChange.notify_all();
if (rpc_worker_group != NULL)
rpc_worker_group->join_all();
delete rpc_dummy_work; rpc_dummy_work = NULL;
@@ -706,6 +712,11 @@ void StopRPCThreads()
delete rpc_io_service; rpc_io_service = NULL;
}
+bool IsRPCRunning()
+{
+ return fRPCRunning;
+}
+
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
{
if (!err)
diff --git a/src/rpcserver.h b/src/rpcserver.h
index e32eb975a1..31badadd6d 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -40,6 +40,8 @@ void StartRPCThreads();
void StartDummyRPCThread();
/* Stop RPC threads */
void StopRPCThreads();
+/* Query whether RPC is running */
+bool IsRPCRunning();
/*
Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index e8c62fd37b..5b83fe900e 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -58,10 +58,6 @@ 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)
@@ -641,16 +637,16 @@ Value getbalance(const Array& params, bool fHelp)
int64_t allFee;
string strSentAccount;
- list<pair<CTxDestination, int64_t> > listReceived;
- list<pair<CTxDestination, int64_t> > listSent;
+ list<COutputEntry> listReceived;
+ list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
- BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
- nBalance += r.second;
+ BOOST_FOREACH(const COutputEntry& r, listReceived)
+ nBalance += r.amount;
}
- BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent)
- nBalance -= r.second;
+ BOOST_FOREACH(const COutputEntry& s, listSent)
+ nBalance -= s.amount;
nBalance -= allFee;
}
return ValueFromAmount(nBalance);
@@ -1133,8 +1129,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
{
int64_t nFee;
string strSentAccount;
- list<pair<CTxDestination, int64_t> > listReceived;
- list<pair<CTxDestination, int64_t> > listSent;
+ list<COutputEntry> listReceived;
+ list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
@@ -1144,15 +1140,16 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
{
- BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
+ BOOST_FOREACH(const COutputEntry& s, listSent)
{
Object entry;
- if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & ISMINE_WATCH_ONLY))
+ if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount));
- MaybePushAddress(entry, s.first);
+ MaybePushAddress(entry, s.destination);
entry.push_back(Pair("category", "send"));
- entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
+ entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
+ entry.push_back(Pair("vout", s.vout));
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
if (fLong)
WalletTxToJSON(wtx, entry);
@@ -1163,18 +1160,18 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
{
- BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
+ BOOST_FOREACH(const COutputEntry& r, listReceived)
{
string account;
- if (pwalletMain->mapAddressBook.count(r.first))
- account = pwalletMain->mapAddressBook[r.first].name;
+ if (pwalletMain->mapAddressBook.count(r.destination))
+ account = pwalletMain->mapAddressBook[r.destination].name;
if (fAllAccounts || (account == strAccount))
{
Object entry;
- if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & ISMINE_WATCH_ONLY))
+ if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account));
- MaybePushAddress(entry, r.first);
+ MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
if (wtx.GetDepthInMainChain() < 1)
@@ -1188,7 +1185,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
{
entry.push_back(Pair("category", "receive"));
}
- entry.push_back(Pair("amount", ValueFromAmount(r.second)));
+ entry.push_back(Pair("amount", ValueFromAmount(r.amount)));
+ entry.push_back(Pair("vout", r.vout));
if (fLong)
WalletTxToJSON(wtx, entry);
ret.push_back(entry);
@@ -1240,6 +1238,7 @@ Value listtransactions(const Array& params, bool fHelp)
" \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n"
" 'move' category for moves outbound. It is positive for the 'receive' category,\n"
" and for the 'move' category for inbound funds.\n"
+ " \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n"
" 'send' category of transactions.\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n"
@@ -1249,12 +1248,6 @@ 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"
@@ -1375,22 +1368,22 @@ Value listaccounts(const Array& params, bool fHelp)
const CWalletTx& wtx = (*it).second;
int64_t nFee;
string strSentAccount;
- list<pair<CTxDestination, int64_t> > listReceived;
- list<pair<CTxDestination, int64_t> > listSent;
+ list<COutputEntry> listReceived;
+ list<COutputEntry> listSent;
int nDepth = wtx.GetDepthInMainChain();
if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0)
continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
- BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
- mapAccountBalances[strSentAccount] -= s.second;
+ BOOST_FOREACH(const COutputEntry& s, listSent)
+ mapAccountBalances[strSentAccount] -= s.amount;
if (nDepth >= nMinDepth)
{
- BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
- if (pwalletMain->mapAddressBook.count(r.first))
- mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second;
+ BOOST_FOREACH(const COutputEntry& r, listReceived)
+ if (pwalletMain->mapAddressBook.count(r.destination))
+ mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount;
else
- mapAccountBalances[""] += r.second;
+ mapAccountBalances[""] += r.amount;
}
}
@@ -1424,18 +1417,13 @@ Value listsinceblock(const Array& params, bool fHelp)
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n"
" outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n"
+ " \"vout\" : n, (numeric) the vout value\n"
" \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"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"
@@ -1514,12 +1502,6 @@ 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"
@@ -1528,6 +1510,7 @@ Value gettransaction(const Array& params, bool fHelp)
" \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
" \"amount\" : x.xxx (numeric) The amount in btc\n"
+ " \"vout\" : n, (numeric) the vout value\n"
" }\n"
" ,...\n"
" ],\n"
diff --git a/src/script.cpp b/src/script.cpp
index 238a25e72d..39ae001db8 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -5,13 +5,14 @@
#include "script.h"
+#include "crypto/ripemd160.h"
+#include "crypto/sha1.h"
+#include "crypto/sha2.h"
#include "core.h"
#include "hash.h"
#include "key.h"
#include "keystore.h"
-#include "crypto/sha1.h"
-#include "crypto/sha2.h"
-#include "crypto/ripemd160.h"
+#include "random.h"
#include "sync.h"
#include "uint256.h"
#include "util.h"
@@ -1097,7 +1098,6 @@ uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsig
// 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)
-
class CSignatureCache
{
private:
diff --git a/src/serialize.h b/src/serialize.h
index 5ac85554c6..f876efd9b5 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -830,6 +830,35 @@ struct ser_streamplaceholder
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData;
+class CSizeComputer
+{
+protected:
+ size_t nSize;
+
+public:
+ int nType;
+ int nVersion;
+
+ CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
+
+ CSizeComputer& write(const char *psz, int nSize)
+ {
+ this->nSize += nSize;
+ return *this;
+ }
+
+ template<typename T>
+ CSizeComputer& operator<<(const T& obj)
+ {
+ ::Serialize(*this, obj, nType, nVersion);
+ return (*this);
+ }
+
+ size_t size() const {
+ return nSize;
+ }
+};
+
/** Double ended buffer combining vector and stream-like interfaces.
*
* >> and << read and write unformatted data using the above serialization templates.
diff --git a/src/sync.h b/src/sync.h
index 077ed59b89..cd319e0171 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -84,6 +84,9 @@ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
+/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
+typedef boost::condition_variable CConditionVariable;
+
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 2cdafa4bdd..69de3b5bb1 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -45,10 +45,6 @@ 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/canonical_tests.cpp b/src/test/canonical_tests.cpp
index 23dd74296c..a9798623ea 100644
--- a/src/test/canonical_tests.cpp
+++ b/src/test/canonical_tests.cpp
@@ -6,12 +6,11 @@
// Unit tests for canonical signatures
//
-
-
-#include "script.h"
-#include "util.h"
#include "data/sig_noncanonical.json.h"
#include "data/sig_canonical.json.h"
+#include "random.h"
+#include "script.h"
+#include "util.h"
#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
@@ -21,7 +20,6 @@
using namespace std;
using namespace json_spirit;
-
// In script_tests.cpp
extern Array read_json(const std::string& jsondata);
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 7bd98fa381..a17278b803 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -5,6 +5,7 @@
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha2.h"
+#include "random.h"
#include "util.h"
#include <vector>
diff --git a/src/test/mruset_tests.cpp b/src/test/mruset_tests.cpp
index 60f11c147a..547cd1090c 100644
--- a/src/test/mruset_tests.cpp
+++ b/src/test/mruset_tests.cpp
@@ -4,6 +4,7 @@
#include "mruset.h"
+#include "random.h"
#include "util.h"
#include <set>
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 423ae4a789..b99797fccb 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -2,15 +2,16 @@
// 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 <iostream>
-
+#include "data/sighash.json.h"
#include "main.h"
-#include "util.h"
+#include "random.h"
#include "serialize.h"
+#include "util.h"
#include "version.h"
-#include "data/sighash.json.h"
+#include <iostream>
+
+#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
@@ -118,7 +119,7 @@ BOOST_AUTO_TEST_SUITE(sighash_tests)
BOOST_AUTO_TEST_CASE(sighash_test)
{
seed_insecure_rand(false);
-
+
#if defined(PRINT_SIGHASH_JSON)
std::cout << "[\n";
std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
@@ -205,10 +206,9 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
continue;
}
-
+
sh = SignatureHash(scriptCode, tx, nIn, nHashType);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
}
BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 11762c6ea0..a123f1d197 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -2,11 +2,13 @@
// 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 "random.h"
#include "util.h"
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
#define SKIPLIST_LENGTH 300000
@@ -98,4 +100,3 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
}
BOOST_AUTO_TEST_SUITE_END()
-
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index bcd2f75f55..443b5853b2 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -4,9 +4,8 @@
#define BOOST_TEST_MODULE Bitcoin Test Suite
-
-
#include "main.h"
+#include "random.h"
#include "txdb.h"
#include "ui_interface.h"
#include "util.h"
@@ -89,4 +88,3 @@ bool ShutdownRequested()
{
return false;
}
-
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 0b071361d8..068b9f29c8 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -4,6 +4,7 @@
#include "util.h"
+#include "random.h"
#include "sync.h"
#include <stdint.h>
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 8a095d26dc..6c3bd9a48d 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -49,6 +49,24 @@ void AddTimeData(const CNetAddr& ip, int64_t nTime)
static CMedianFilter<int64_t> vTimeOffsets(200,0);
vTimeOffsets.input(nOffsetSample);
LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+
+ // There is a known issue here (see issue #4521):
+ //
+ // - The structure vTimeOffsets contains up to 200 elements, after which
+ // any new element added to it will not increase its size, replacing the
+ // oldest element.
+ //
+ // - The condition to update nTimeOffset includes checking whether the
+ // number of elements in vTimeOffsets is odd, which will never happen after
+ // there are 200 elements.
+ //
+ // But in this case the 'bug' is protective against some attacks, and may
+ // actually explain why we've never seen attacks which manipulate the
+ // clock offset.
+ //
+ // So we should hold off on fixing this and clean it up as part of
+ // a timing cleanup that strengthens it in a number of other ways.
+ //
if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
{
int64_t nMedian = vTimeOffsets.median();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index a852de5da8..164e2741a2 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -420,6 +420,7 @@ 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);
@@ -601,14 +602,15 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash)
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) {
- if (base->GetCoins(txid, coins))
- return true;
+ // If an entry in the mempool exists, always return that one, as it's guaranteed to never
+ // conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
+ // transactions. First checking the underlying cache risks returning a pruned entry instead.
CTransaction tx;
if (mempool.lookup(txid, tx)) {
coins = CCoins(tx, MEMPOOL_HEIGHT);
return true;
}
- return false;
+ return (base->GetCoins(txid, coins) && !coins.IsPruned());
}
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) {
diff --git a/src/ui_interface.h b/src/ui_interface.h
index e9fcd91d41..b3df2b5a85 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -21,8 +21,7 @@ enum ChangeType
{
CT_NEW,
CT_UPDATED,
- CT_DELETED,
- CT_GOT_CONFLICT
+ CT_DELETED
};
/** Signals for UI communication. */
@@ -63,8 +62,6 @@ public:
/** Force blocking, modal message box dialog (not just OS notification) */
MODAL = 0x10000000U,
- /** Don't bring GUI to foreground. Use for messages during initialization */
- NOSHOWGUI = 0x20000000U,
/** Predefined combinations for certain default usage cases */
MSG_INFORMATION = ICON_INFORMATION,
diff --git a/src/uint256.cpp b/src/uint256.cpp
index 3392f1e9bc..08c05594fd 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -290,3 +290,46 @@ uint32_t uint256::GetCompact(bool fNegative) const
nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0);
return nCompact;
}
+
+static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c)
+{
+ // Taken from lookup3, by Bob Jenkins.
+ a -= c; a ^= ((c << 4) | (c >> 28)); c += b;
+ b -= a; b ^= ((a << 6) | (a >> 26)); a += c;
+ c -= b; c ^= ((b << 8) | (b >> 24)); b += a;
+ a -= c; a ^= ((c << 16) | (c >> 16)); c += b;
+ b -= a; b ^= ((a << 19) | (a >> 13)); a += c;
+ c -= b; c ^= ((b << 4) | (b >> 28)); b += a;
+}
+
+static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c)
+{
+ // Taken from lookup3, by Bob Jenkins.
+ c ^= b; c -= ((b << 14) | (b >> 18));
+ a ^= c; a -= ((c << 11) | (c >> 21));
+ b ^= a; b -= ((a << 25) | (a >> 7));
+ c ^= b; c -= ((b << 16) | (b >> 16));
+ a ^= c; a -= ((c << 4) | (c >> 28));
+ b ^= a; b -= ((a << 14) | (a >> 18));
+ c ^= b; c -= ((b << 24) | (b >> 8));
+}
+
+uint64_t uint256::GetHash(const uint256 &salt) const
+{
+ uint32_t a, b, c;
+ a = b = c = 0xdeadbeef + (WIDTH << 2);
+
+ a += pn[0] ^ salt.pn[0];
+ b += pn[1] ^ salt.pn[1];
+ c += pn[2] ^ salt.pn[2];
+ HashMix(a, b, c);
+ a += pn[3] ^ salt.pn[3];
+ b += pn[4] ^ salt.pn[4];
+ c += pn[5] ^ salt.pn[5];
+ HashMix(a, b, c);
+ a += pn[6] ^ salt.pn[6];
+ b += pn[7] ^ salt.pn[7];
+ HashFinal(a, b, c);
+
+ return ((((uint64_t)b) << 32) | c);
+}
diff --git a/src/uint256.h b/src/uint256.h
index 82db7758c9..ad0a56f447 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -21,7 +21,7 @@ public:
template<unsigned int BITS>
class base_uint
{
-private:
+protected:
enum { WIDTH=BITS/32 };
uint32_t pn[WIDTH];
public:
@@ -322,6 +322,8 @@ public:
// implementation accident.
uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL);
uint32_t GetCompact(bool fNegative = false) const;
+
+ uint64_t GetHash(const uint256& salt) const;
};
#endif
diff --git a/src/util.cpp b/src/util.cpp
index 91ac8833d5..d3fa5182f3 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -6,6 +6,7 @@
#include "util.h"
#include "chainparamsbase.h"
+#include "random.h"
#include "sync.h"
#include "uint256.h"
#include "version.h"
@@ -141,90 +142,6 @@ public:
}
instance_of_cinit;
-
-
-
-
-
-
-
-void RandAddSeed()
-{
- // Seed with CPU performance counter
- int64_t nCounter = GetPerformanceCounter();
- RAND_add(&nCounter, sizeof(nCounter), 1.5);
- memset(&nCounter, 0, sizeof(nCounter));
-}
-
-void RandAddSeedPerfmon()
-{
- RandAddSeed();
-
- // This can take up to 2 seconds, so only do it every 10 minutes
- static int64_t nLastPerfmon;
- if (GetTime() < nLastPerfmon + 10 * 60)
- return;
- nLastPerfmon = GetTime();
-
-#ifdef WIN32
- // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
- // Seed with the entire set of perfmon data
- std::vector <unsigned char> vData(250000,0);
- long ret = 0;
- unsigned long nSize = 0;
- const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
- while (true)
- {
- nSize = vData.size();
- ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize);
- if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
- break;
- vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially
- }
- RegCloseKey(HKEY_PERFORMANCE_DATA);
- if (ret == ERROR_SUCCESS)
- {
- RAND_add(begin_ptr(vData), nSize, nSize/100.0);
- OPENSSL_cleanse(begin_ptr(vData), nSize);
- LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
- } else {
- static bool warned = false; // Warn only once
- if (!warned)
- {
- LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
- warned = true;
- }
- }
-#endif
-}
-
-uint64_t GetRand(uint64_t nMax)
-{
- if (nMax == 0)
- return 0;
-
- // The range of the random source must be a multiple of the modulus
- // to give every possible output value an equal possibility
- uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax;
- uint64_t nRand = 0;
- do
- RAND_bytes((unsigned char*)&nRand, sizeof(nRand));
- while (nRand >= nRange);
- return (nRand % nMax);
-}
-
-int GetRandInt(int nMax)
-{
- return GetRand(nMax);
-}
-
-uint256 GetRandHash()
-{
- uint256 hash;
- RAND_bytes((unsigned char*)&hash, sizeof(hash));
- return hash;
-}
-
// LogPrintf() has been broken a couple of times now
// by well-meaning people adding mutexes in the most straightforward way.
// It breaks because it may be called by global destructors during shutdown.
@@ -288,7 +205,7 @@ int LogPrintStr(const std::string &str)
// print to console
ret = fwrite(str.data(), 1, str.size(), stdout);
}
- else if (fPrintToDebugLog)
+ else if (fPrintToDebugLog && AreBaseParamsConfigured())
{
static bool fStartedNewLine = true;
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
@@ -1192,27 +1109,6 @@ void SetMockTime(int64_t nMockTimeIn)
nMockTime = nMockTimeIn;
}
-uint32_t insecure_rand_Rz = 11;
-uint32_t insecure_rand_Rw = 11;
-void seed_insecure_rand(bool fDeterministic)
-{
- //The seed values have some unlikely fixed points which we avoid.
- if(fDeterministic)
- {
- insecure_rand_Rz = insecure_rand_Rw = 11;
- } else {
- uint32_t tmp;
- do {
- RAND_bytes((unsigned char*)&tmp, 4);
- } while(tmp == 0 || tmp == 0x9068ffffU);
- insecure_rand_Rz = tmp;
- do {
- RAND_bytes((unsigned char*)&tmp, 4);
- } while(tmp == 0 || tmp == 0x464fffffU);
- insecure_rand_Rw = tmp;
- }
-}
-
string FormatVersion(int nVersion)
{
if (nVersion%100 == 0)
diff --git a/src/util.h b/src/util.h
index 60db71bfd0..db2005337b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -90,8 +90,6 @@ inline void MilliSleep(int64_t n)
#endif
}
-
-
extern std::map<std::string, std::string> mapArgs;
extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
extern bool fDebug;
@@ -103,8 +101,6 @@ extern bool fLogTimestamps;
extern bool fLogIPs;
extern volatile bool fReopenDebugLog;
-void RandAddSeed();
-void RandAddSeedPerfmon();
void SetupEnvironment();
/* Return true if log accepts specified category */
@@ -187,23 +183,12 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
boost::filesystem::path GetTempPath();
void ShrinkDebugFile();
-int GetRandInt(int nMax);
-uint64_t GetRand(uint64_t nMax);
-uint256 GetRandHash();
int64_t GetTime();
void SetMockTime(int64_t nMockTimeIn);
std::string FormatFullVersion();
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);
void runCommand(std::string strCommand);
-
-
-
-
-
-
-
-
inline std::string i64tostr(int64_t n)
{
return strprintf("%d", n);
@@ -289,19 +274,6 @@ inline std::string HexStr(const T& vch, bool fSpaces=false)
*/
std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0);
-inline int64_t GetPerformanceCounter()
-{
- int64_t nCounter = 0;
-#ifdef WIN32
- QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
-#else
- timeval t;
- gettimeofday(&t, NULL);
- nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec;
-#endif
- return nCounter;
-}
-
inline int64_t GetTimeMillis()
{
return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) -
@@ -371,28 +343,6 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue);
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
/**
- * MWC RNG of George Marsaglia
- * This is intended to be fast. It has a period of 2^59.3, though the
- * least significant 16 bits only have a period of about 2^30.1.
- *
- * @return random value
- */
-extern uint32_t insecure_rand_Rz;
-extern uint32_t insecure_rand_Rw;
-static inline uint32_t insecure_rand(void)
-{
- insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16);
- insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16);
- return (insecure_rand_Rw << 16) + insecure_rand_Rz;
-}
-
-/**
- * Seed insecure_rand using the random pool.
- * @param Deterministic Use a determinstic seed
- */
-void seed_insecure_rand(bool fDeterministic=false);
-
-/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
diff --git a/src/version.h b/src/version.h
index 3d1abacb94..85c5dbf8d7 100644
--- a/src/version.h
+++ b/src/version.h
@@ -28,7 +28,7 @@ extern const std::string CLIENT_DATE;
static const int PROTOCOL_VERSION = 70002;
-// intial proto version, to be increased after version/verack negotiation
+// initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
// disconnect from peers older than this proto version
@@ -45,7 +45,7 @@ static const int NOBLKS_VERSION_END = 32400;
// BIP 0031, pong message, is enabled for all versions AFTER this one
static const int BIP0031_VERSION = 60000;
-// "mempool" command, enhanced "getdata" behavior starts with this version:
+// "mempool" command, enhanced "getdata" behavior starts with this version
static const int MEMPOOL_GD_VERSION = 60002;
#endif
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 8fdc5f4b20..7c04743c0f 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -12,7 +12,6 @@
#include "timedata.h"
#include <boost/algorithm/string/replace.hpp>
-#include <openssl/rand.h>
using namespace std;
@@ -275,7 +274,7 @@ bool CWallet::SetMaxVersion(int nVersion)
return true;
}
-set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const
+set<uint256> CWallet::GetConflicts(const uint256& txid) const
{
set<uint256> result;
AssertLockHeld(cs_wallet);
@@ -293,8 +292,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent)
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)
- if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second)))
- result.insert(it->second);
+ result.insert(it->second);
}
return result;
}
@@ -323,7 +321,6 @@ 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
@@ -384,13 +381,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
RandAddSeedPerfmon();
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
- RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
+ if (!GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE))
+ return false;
CMasterKey kMasterKey;
-
RandAddSeedPerfmon();
+
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
- RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
+ if (!GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE))
+ return false;
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
@@ -609,28 +608,6 @@ 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", "");
@@ -653,12 +630,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
AssertLockHeld(cs_wallet);
bool fExisted = mapWallet.count(tx.GetHash());
if (fExisted && !fUpdate) return false;
-
- bool fIsConflicting = IsConflicting(tx);
- if (fIsConflicting)
- nConflictsReceived++;
-
- if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting)
+ if (fExisted || IsMine(tx) || IsFromMe(tx))
{
CWalletTx wtx(this,tx);
// Get merkle branch if transaction was found in a block
@@ -797,8 +769,8 @@ int CWalletTx::GetRequestCount() const
return nRequests;
}
-void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
- list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const
+void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
+ list<COutputEntry>& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const
{
nFee = 0;
listReceived.clear();
@@ -814,10 +786,10 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
}
// Sent/received.
- BOOST_FOREACH(const CTxOut& txout, vout)
+ for (unsigned int i = 0; i < vout.size(); ++i)
{
+ const CTxOut& txout = vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
-
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
@@ -839,13 +811,15 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
address = CNoDestination();
}
+ COutputEntry output = {address, txout.nValue, (int)i};
+
// If we are debited by the transaction, add the output as a "sent" entry
if (nDebit > 0)
- listSent.push_back(make_pair(address, txout.nValue));
+ listSent.push_back(output);
// If we are receiving the output, add it as a "received" entry
- if (fIsMine)
- listReceived.push_back(make_pair(address, txout.nValue));
+ if (fIsMine & filter)
+ listReceived.push_back(output);
}
}
@@ -857,29 +831,29 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
int64_t allFee;
string strSentAccount;
- list<pair<CTxDestination, int64_t> > listReceived;
- list<pair<CTxDestination, int64_t> > listSent;
+ list<COutputEntry> listReceived;
+ list<COutputEntry> listSent;
GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (strAccount == strSentAccount)
{
- BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& s, listSent)
- nSent += s.second;
+ BOOST_FOREACH(const COutputEntry& s, listSent)
+ nSent += s.amount;
nFee = allFee;
}
{
LOCK(pwallet->cs_wallet);
- BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
+ BOOST_FOREACH(const COutputEntry& r, listReceived)
{
- if (pwallet->mapAddressBook.count(r.first))
+ if (pwallet->mapAddressBook.count(r.destination))
{
- map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
+ map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination);
if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount)
- nReceived += r.second;
+ nReceived += r.amount;
}
else if (strAccount.empty())
{
- nReceived += r.second;
+ nReceived += r.amount;
}
}
}
@@ -945,7 +919,7 @@ void CWallet::ReacceptWalletTransactions()
int nDepth = wtx.GetDepthInMainChain();
- if (!wtx.IsCoinBase() && nDepth < 0 && (IsMine(wtx) || IsFromMe(wtx)))
+ if (!wtx.IsCoinBase() && nDepth < 0)
{
// Try to add to memory pool
LOCK(mempool.cs);
@@ -965,13 +939,13 @@ void CWalletTx::RelayWalletTransaction()
}
}
-set<uint256> CWalletTx::GetConflicts(bool includeEquivalent) const
+set<uint256> CWalletTx::GetConflicts() const
{
set<uint256> result;
if (pwallet != NULL)
{
uint256 myHash = GetHash();
- result = pwallet->GetConflicts(myHash, includeEquivalent);
+ result = pwallet->GetConflicts(myHash);
result.erase(myHash);
}
return result;
@@ -1075,7 +1049,7 @@ int64_t CWallet::GetWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@@ -1091,7 +1065,7 @@ int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@@ -1106,7 +1080,7 @@ int64_t CWallet::GetImmatureWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
diff --git a/src/wallet.h b/src/wallet.h
index ab9550a984..73fcfa24e0 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -144,9 +144,6 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
- // Increment to cause UI refresh, similar to new block
- int64_t nConflictsReceived;
-
CWallet()
{
SetNull();
@@ -169,7 +166,6 @@ public:
nNextResend = 0;
nLastResend = 0;
nTimeFirstKey = 0;
- nConflictsReceived = 0;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -322,13 +318,6 @@ public:
{
return (GetDebit(tx, ISMINE_ALL) > 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 isminefilter& filter) const
{
int64_t nDebit = 0;
@@ -401,7 +390,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, bool includeEquivalent) const;
+ std::set<uint256> GetConflicts(const uint256& txid) const;
/** Address book entry changed.
* @note called with lock cs_wallet held.
@@ -467,6 +456,12 @@ static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
mapValue["n"] = i64tostr(nOrderPos);
}
+struct COutputEntry
+{
+ CTxDestination destination;
+ int64_t amount;
+ int vout;
+};
/** A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.
@@ -761,8 +756,8 @@ public:
return nChangeCached;
}
- void GetAmounts(std::list<std::pair<CTxDestination, int64_t> >& listReceived,
- std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const;
+ void GetAmounts(std::list<COutputEntry>& listReceived,
+ std::list<COutputEntry>& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const;
void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived,
int64_t& nSent, int64_t& nFee, const isminefilter& filter) const;
@@ -806,7 +801,7 @@ public:
void RelayWalletTransaction();
- std::set<uint256> GetConflicts(bool includeEquivalent=true) const;
+ std::set<uint256> GetConflicts() const;
};