aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac9
-rw-r--r--contrib/debian/examples/bitcoin.conf2
-rw-r--r--contrib/debian/manpages/bitcoin-qt.15
-rw-r--r--contrib/debian/manpages/bitcoind.12
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rwxr-xr-xcontrib/pyminer/pyminer.py457
-rw-r--r--doc/tor.md5
-rw-r--r--src/Makefile.qt.include1
-rw-r--r--src/allocators.h2
-rw-r--r--src/main.cpp3
-rw-r--r--src/qt/bitcoinamountfield.cpp272
-rw-r--r--src/qt/bitcoinamountfield.h14
-rw-r--r--src/qt/bitcoinunits.cpp29
-rw-r--r--src/qt/bitcoinunits.h7
-rw-r--r--src/qt/sendcoinsentry.cpp18
-rw-r--r--src/rpcdump.cpp26
-rw-r--r--src/timedata.cpp18
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/version.h4
19 files changed, 487 insertions, 396 deletions
diff --git a/configure.ac b/configure.ac
index cedc34e520..719b06da67 100644
--- a/configure.ac
+++ b/configure.ac
@@ -392,6 +392,8 @@ AC_TRY_COMPILE([#include <sys/socket.h>],
[ AC_MSG_RESULT(no)]
)
+AC_SEARCH_LIBS([clock_gettime],[rt])
+
LEVELDB_CPPFLAGS=
LIBLEVELDB=
LIBMEMENV=
@@ -460,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([
@@ -486,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([
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 cd478b1875..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)
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/pyminer.py b/contrib/pyminer/pyminer.py
index 0a2932d66e..706a10b39d 100755
--- a/contrib/pyminer/pyminer.py
+++ b/contrib/pyminer/pyminer.py
@@ -5,248 +5,265 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
+import sys
+from multiprocessing import Process
import time
-import json
-import pprint
-import hashlib
import struct
-import re
+import hashlib
import base64
+import re
import httplib
-import sys
-from multiprocessing import Process
+import json
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)
+ object_id = 1
+
+ def __init__(self, host, port, username, password):
+ authpair = "{0}:{1}".format(username, password)
+ self.authhdr = "Basic {0}".format(base64.b64encode(authpair))
+ self.conn = httplib.HTTPConnection(host, port, strict=False, timeout=30)
+
+ def rpc(self, method, params=None):
+ self.object_id += 1
+ obj = {'version' : '1.1',
+ 'method' : method,
+ 'id' : self.object_id,
+ 'params' : params or []}
+
+ 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
+ return x & 0xffffffffL
def bytereverse(x):
- return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
- (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
+ 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)
+ 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)
+ 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 __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')
+ long_hash = long(hash_str, 16)
+
+ # proof-of-work test: hash < target
+ if long_hash < target:
+ print(time.asctime(), "PROOF-OF-WORK found: "
+ "{0:064x}".format(long_hash))
+ return (nonce + 1, nonce_bin)
+ else:
+ print(time.asctime(), "PROOF-OF-WORK false"
+ "positive {0:064x}".format(long_hash))
+
+ 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".format(
+ 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 not None:
+
+ while True:
+ self.iterate(rpc)
+
+ self.conn.close()
+
def miner_thread(id):
- miner = Miner(id)
- miner.loop()
+ 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'])
-
+ if len(sys.argv) != 2:
+ print("Usage: pyminer.py CONFIG-FILE")
+ sys.exit(1)
+
+ with open(sys.argv[1]) as f:
+
+ 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)
+
+ settings.setdefault('host', '127.0.0.1')
+ settings.setdefault('port', 8332)
+ settings.setdefault('threads', 1)
+ settings.setdefault('hashmeter', 0)
+ settings.setdefault('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'])
+
+ thread_list = []
+
+ for thread_id in range(settings['threads']):
+ p = Process(target=miner_thread, args=(thread_id,))
+ p.start()
+ thread_list.append(p)
+ time.sleep(1) # stagger threads
+
+ print(settings['threads'], "mining threads started")
+
+ print(time.asctime(), "Miner Starts - {0}:{1}".format(settings['host'],
+ settings['port']))
+ try:
+ for thread_process in thread_list:
+ thread_process.join()
+ except KeyboardInterrupt:
+ pass
+
+ print(time.asctime(), "Miner Stops - {0}:{1}".format(settings['host'],
+ settings['port']))
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/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/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/main.cpp b/src/main.cpp
index 84178b16e2..06ce15b5b3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2016,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;
@@ -2026,6 +2026,7 @@ static CBlockIndex* FindMostWorkChain() {
setBlockIndexValid.erase(pindexFailed);
pindexFailed = pindexFailed->pprev;
}
+ setBlockIndexValid.erase(pindexTest);
fInvalidAncestor = true;
break;
}
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index e047c278b7..6466039013 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -9,63 +9,185 @@
#include "qvaluecombobox.h"
#include <QApplication>
-#include <QDoubleSpinBox>
+#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
-#include <qmath.h> // for qPow()
+#include <QLineEdit>
-// QDoubleSpinBox that shows SI-style thin space thousands separators
-class AmountSpinBox: public QDoubleSpinBox
+/** 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):
- QDoubleSpinBox(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);
+ }
}
- QString textFromValue(double value) const
+
+ qint64 value(bool *valid_out=0) const
{
- QStringList parts = QDoubleSpinBox::textFromValue(value).split(".");
- QString quotient_str = parts[0];
- QString remainder_str;
- if(parts.size() > 1)
- remainder_str = parts[1];
-
- // Code duplication between here and BitcoinUnits::format
- // TODO: Figure out how to share this code
- QChar thin_sp(THIN_SP_CP);
- int q_size = quotient_str.size();
- if (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 (r_size > 4)
- for (int i = 3, adj = 0; i < r_size; i += 3, adj++)
- remainder_str.insert(i + adj, thin_sp);
-
- if(remainder_str.isEmpty())
- return quotient_str;
+ 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
- return quotient_str + QString(".") + remainder_str;
+ clear();
}
- QValidator::State validate (QString &text, int &pos) const
+
+ void setSingleStep(qint64 step)
{
- QString s(BitcoinUnits::removeSpaces(text));
- return QDoubleSpinBox::validate(s, pos);
+ 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;
}
- double valueFromText(const QString& text) const
+
+protected:
+ bool event(QEvent *event)
{
- return QDoubleSpinBox::valueFromText(BitcoinUnits::removeSpaces(text));
+ 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 AmountSpinBox(this);
amount->setLocale(QLocale::c());
amount->installEventFilter(this);
@@ -85,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(BitcoinUnits::removeSpaces(text).toDouble());
-}
-
void BitcoinAmountField::clear()
{
amount->clear();
@@ -108,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;
}
@@ -129,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)
@@ -144,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);
}
@@ -167,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)
@@ -195,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)
@@ -226,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/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 21aed235cf..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):
@@ -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)
@@ -250,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 7fa24c8542..be9dca6012 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -82,10 +82,6 @@ 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
@@ -120,6 +116,9 @@ public:
return text;
}
+ //! Return maximum number of base units (Satoshis)
+ static qint64 maxMoney();
+
private:
QList<BitcoinUnits::Unit> unitlist;
};
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/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/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 ebb1369e31..164e2741a2 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -602,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/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