aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--README.md6
-rw-r--r--configure.ac8
-rw-r--r--contrib/debian/control5
-rw-r--r--qa/rpc-tests/README.md4
-rwxr-xr-xqa/rpc-tests/listtransactions.py183
-rw-r--r--qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py30
-rwxr-xr-xqa/rpc-tests/receivedby.py281
-rwxr-xr-xqa/rpc-tests/skeleton.py83
-rwxr-xr-xqa/rpc-tests/smartfees.py195
-rwxr-xr-xqa/rpc-tests/test_framework.py92
-rw-r--r--qa/rpc-tests/util.py15
-rwxr-xr-xshare/qt/extract_strings_qt.py5
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.qt.include11
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/checkpoints.cpp2
-rw-r--r--src/core.cpp7
-rw-r--r--src/init.cpp56
-rw-r--r--src/keystore.cpp12
-rw-r--r--src/keystore.h19
-rw-r--r--src/main.cpp196
-rw-r--r--src/main.h24
-rw-r--r--src/net.cpp16
-rw-r--r--src/net.h12
-rw-r--r--src/netbase.cpp182
-rw-r--r--src/netbase.h6
-rw-r--r--src/pow.cpp2
-rw-r--r--src/qt/bitcoin.qrc3
-rw-r--r--src/qt/bitcoingui.cpp8
-rw-r--r--src/qt/bitcoingui.h6
-rw-r--r--src/qt/bitcoinstrings.cpp79
-rw-r--r--src/qt/bitcoinunits.cpp11
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/clientmodel.cpp2
-rw-r--r--src/qt/coincontroldialog.cpp23
-rw-r--r--src/qt/forms/optionsdialog.ui20
-rw-r--r--src/qt/forms/overviewpage.ui533
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/optionsmodel.cpp28
-rw-r--r--src/qt/overviewpage.cpp34
-rw-r--r--src/qt/overviewpage.h6
-rw-r--r--src/qt/paymentserver.cpp20
-rw-r--r--src/qt/res/icons/unit_btc.pngbin0 -> 2107 bytes
-rw-r--r--src/qt/res/icons/unit_mbtc.pngbin0 -> 2107 bytes
-rw-r--r--src/qt/res/icons/unit_ubtc.pngbin0 -> 2107 bytes
-rw-r--r--src/qt/sendcoinsdialog.cpp13
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/transactiondesc.cpp54
-rw-r--r--src/qt/transactionrecord.cpp26
-rw-r--r--src/qt/transactionrecord.h3
-rw-r--r--src/qt/transactiontablemodel.cpp11
-rw-r--r--src/qt/walletmodel.cpp39
-rw-r--r--src/qt/walletmodel.h9
-rw-r--r--src/qt/winshutdownmonitor.cpp19
-rw-r--r--src/rpcclient.cpp8
-rw-r--r--src/rpcdump.cpp60
-rw-r--r--src/rpcmining.cpp2
-rw-r--r--src/rpcmisc.cpp54
-rw-r--r--src/rpcnet.cpp2
-rw-r--r--src/rpcprotocol.cpp16
-rw-r--r--src/rpcrawtransaction.cpp5
-rw-r--r--src/rpcserver.cpp1
-rw-r--r--src/rpcserver.h1
-rw-r--r--src/rpcwallet.cpp133
-rw-r--r--src/script.cpp44
-rw-r--r--src/script.h15
-rw-r--r--src/test/bloom_tests.cpp2
-rw-r--r--src/test/skiplist_tests.cpp56
-rw-r--r--src/test/test_bitcoin.cpp2
-rw-r--r--src/test/wallet_tests.cpp2
-rw-r--r--src/util.cpp9
-rw-r--r--src/util.h1
-rw-r--r--src/wallet.cpp162
-rw-r--r--src/wallet.h151
-rw-r--r--src/walletdb.cpp19
-rw-r--r--src/walletdb.h3
79 files changed, 1881 insertions, 1294 deletions
diff --git a/.gitignore b/.gitignore
index 564fe68fd..4169a2d96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,13 @@ src/config/stamp-h1
src/build-aux/
share/setup.nsi
share/qt/Info.plist
+# Libtool
+libtool
+src/m4/libtool.m4
+src/m4/ltoptions.m4
+src/m4/ltsugar.m4
+src/m4/ltversion.m4
+src/m4/lt~obsolete.m4
src/qt/*.moc
src/qt/moc_*.cpp
diff --git a/README.md b/README.md
index 7c2fe12f3..081af80dc 100644
--- a/README.md
+++ b/README.md
@@ -76,12 +76,14 @@ Translations
Changes to translations as well as new translations can be submitted to
[Bitcoin Core's Transifex page](https://www.transifex.com/projects/p/bitcoin/).
-Periodically the translations are pulled from Transifex and merged into the git repository. See the
+Translations are periodically pulled from Transifex and merged into the git repository. See the
[translation process](doc/translation_process.md) for details on how this works.
-**Important**: We do not accept translation changes as github pull request because the next
+**Important**: We do not accept translation changes as GitHub pull requests because the next
pull from Transifex would automatically overwrite them again.
+Translators should also subscribe to the [mailing list](https://groups.google.com/forum/#!forum/bitcoin-translators).
+
Development tips and tricks
---------------------------
diff --git a/configure.ac b/configure.ac
index 2a4636e36..d3500b4d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,10 +193,13 @@ case $host in
AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing))
AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing))
- AX_CHECK_LINK_FLAG([[-static]],[LDFLAGS="$LDFLAGS -static"])
AX_CHECK_LINK_FLAG([[-static-libgcc]],[LDFLAGS="$LDFLAGS -static-libgcc"])
AX_CHECK_LINK_FLAG([[-static-libstdc++]],[LDFLAGS="$LDFLAGS -static-libstdc++"])
+ # -static is interpreted by libtool, where it has a different meaning.
+ # In libtool-speak, it's -all-static.
+ AX_CHECK_LINK_FLAG([[-static]],[LDFLAGS="$LDFLAGS -static"; LIBTOOL_LDFLAGS="$LIBTOOL_LDFLAGS -all-static"])
+
AC_PATH_PROG([MAKENSIS], [makensis], none)
if test x$MAKENSIS = xnone; then
AC_MSG_WARN("makensis not found. Cannot create installer.")
@@ -370,6 +373,8 @@ if test x$TARGET_OS = xdarwin; then
fi
AC_CHECK_HEADERS([endian.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h])
+AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])])
+AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])])
AC_CHECK_DECLS([le32toh, le64toh, htole32, htole64, be32toh, be64toh, htobe32, htobe64],,,
[#if HAVE_ENDIAN_H
@@ -696,6 +701,7 @@ AC_SUBST(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE)
AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR)
+AC_SUBST(LIBTOOL_LDFLAGS)
AC_SUBST(USE_UPNP)
AC_SUBST(USE_QRCODE)
AC_SUBST(INCLUDES)
diff --git a/contrib/debian/control b/contrib/debian/control
index a04e88d4e..ac635f43e 100644
--- a/contrib/debian/control
+++ b/contrib/debian/control
@@ -39,8 +39,9 @@ Description: peer-to-peer network based digital currency - daemon
Full transaction history is stored locally at each client. This
requires 20+ GB of space, slowly growing.
.
- This package provides bitcoind, a combined daemon and CLI tool to
- interact with the daemon.
+
+ This package provides the daemon, bitcoind, and the CLI tool
+ bitcoin-cli to interact with the daemon.
Package: bitcoin-qt
Architecture: any
diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md
index 616c0525f..3e916a768 100644
--- a/qa/rpc-tests/README.md
+++ b/qa/rpc-tests/README.md
@@ -6,8 +6,8 @@ Git subtree of [https://github.com/jgarzik/python-bitcoinrpc](https://github.com
Changes to python-bitcoinrpc should be made upstream, and then
pulled here using git subtree.
-### [skeleton.py](skeleton.py)
-Copy this to create new regression tests.
+### [test_framework.py](test_framework.py)
+Base class for new regression tests.
### [listtransactions.py](listtransactions.py)
Tests for the listtransactions RPC call.
diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py
index f16095c12..50385b437 100755
--- a/qa/rpc-tests/listtransactions.py
+++ b/qa/rpc-tests/listtransactions.py
@@ -5,17 +5,7 @@
# Exercise the listtransactions API
-# Add python-bitcoinrpc to module search path:
-import os
-import sys
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
-
-import json
-import shutil
-import subprocess
-import tempfile
-import traceback
-
+from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import *
@@ -41,116 +31,67 @@ def check_array_result(object_array, to_match, expected):
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
-def run_test(nodes):
- # Simple send, 0 to 1:
- txid = nodes[0].sendtoaddress(nodes[1].getnewaddress(), 0.1)
- sync_mempools(nodes)
- check_array_result(nodes[0].listtransactions(),
- {"txid":txid},
- {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
- check_array_result(nodes[1].listtransactions(),
- {"txid":txid},
- {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
- # mine a block, confirmations should change:
- nodes[0].setgenerate(True, 1)
- sync_blocks(nodes)
- check_array_result(nodes[0].listtransactions(),
- {"txid":txid},
- {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
- check_array_result(nodes[1].listtransactions(),
- {"txid":txid},
- {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
-
- # send-to-self:
- txid = nodes[0].sendtoaddress(nodes[0].getnewaddress(), 0.2)
- check_array_result(nodes[0].listtransactions(),
- {"txid":txid, "category":"send"},
- {"amount":Decimal("-0.2")})
- check_array_result(nodes[0].listtransactions(),
- {"txid":txid, "category":"receive"},
- {"amount":Decimal("0.2")})
-
- # sendmany from node1: twice to self, twice to node2:
- send_to = { nodes[0].getnewaddress() : 0.11, nodes[1].getnewaddress() : 0.22,
- nodes[0].getaccountaddress("from1") : 0.33, nodes[1].getaccountaddress("toself") : 0.44 }
- txid = nodes[1].sendmany("", send_to)
- sync_mempools(nodes)
- check_array_result(nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.11")},
- {"txid":txid} )
- check_array_result(nodes[0].listtransactions(),
- {"category":"receive","amount":Decimal("0.11")},
- {"txid":txid} )
- check_array_result(nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.22")},
- {"txid":txid} )
- check_array_result(nodes[1].listtransactions(),
- {"category":"receive","amount":Decimal("0.22")},
- {"txid":txid} )
- check_array_result(nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.33")},
- {"txid":txid} )
- check_array_result(nodes[0].listtransactions(),
- {"category":"receive","amount":Decimal("0.33")},
- {"txid":txid, "account" : "from1"} )
- check_array_result(nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.44")},
- {"txid":txid, "account" : ""} )
- check_array_result(nodes[1].listtransactions(),
- {"category":"receive","amount":Decimal("0.44")},
- {"txid":txid, "account" : "toself"} )
-
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- nodes = start_nodes(2, options.tmpdir)
- connect_nodes(nodes[1], 0)
+class ListTransactionsTest(BitcoinTestFramework):
+
+ def run_test(self, nodes):
+ # Simple send, 0 to 1:
+ txid = nodes[0].sendtoaddress(nodes[1].getnewaddress(), 0.1)
+ sync_mempools(nodes)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid},
+ {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
+ check_array_result(nodes[1].listtransactions(),
+ {"txid":txid},
+ {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
+ # mine a block, confirmations should change:
+ nodes[0].setgenerate(True, 1)
sync_blocks(nodes)
-
- run_test(nodes)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(e))
- traceback.print_tb(sys.exc_info()[2])
-
- if not options.nocleanup:
- print("Cleaning up")
- stop_nodes(nodes)
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
-
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid},
+ {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
+ check_array_result(nodes[1].listtransactions(),
+ {"txid":txid},
+ {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
+
+ # send-to-self:
+ txid = nodes[0].sendtoaddress(nodes[0].getnewaddress(), 0.2)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid, "category":"send"},
+ {"amount":Decimal("-0.2")})
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid, "category":"receive"},
+ {"amount":Decimal("0.2")})
+
+ # sendmany from node1: twice to self, twice to node2:
+ send_to = { nodes[0].getnewaddress() : 0.11, nodes[1].getnewaddress() : 0.22,
+ nodes[0].getaccountaddress("from1") : 0.33, nodes[1].getaccountaddress("toself") : 0.44 }
+ txid = nodes[1].sendmany("", send_to)
+ sync_mempools(nodes)
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.11")},
+ {"txid":txid} )
+ check_array_result(nodes[0].listtransactions(),
+ {"category":"receive","amount":Decimal("0.11")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.22")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"receive","amount":Decimal("0.22")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.33")},
+ {"txid":txid} )
+ check_array_result(nodes[0].listtransactions(),
+ {"category":"receive","amount":Decimal("0.33")},
+ {"txid":txid, "account" : "from1"} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.44")},
+ {"txid":txid, "account" : ""} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"receive","amount":Decimal("0.44")},
+ {"txid":txid, "account" : "toself"} )
if __name__ == '__main__':
- main()
+ ListTransactionsTest().main()
+
diff --git a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py b/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py
index c2e5406c2..bc7d655fd 100644
--- a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py
+++ b/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py
@@ -39,8 +39,9 @@ try:
except ImportError:
import httplib
import base64
-import json
import decimal
+import json
+import logging
try:
import urllib.parse as urlparse
except ImportError:
@@ -50,6 +51,7 @@ USER_AGENT = "AuthServiceProxy/0.1"
HTTP_TIMEOUT = 30
+log = logging.getLogger("BitcoinRPC")
class JSONRPCException(Exception):
def __init__(self, rpc_error):
@@ -57,7 +59,14 @@ class JSONRPCException(Exception):
self.error = rpc_error
+def EncodeDecimal(o):
+ if isinstance(o, decimal.Decimal):
+ return round(o, 8)
+ raise TypeError(repr(o) + " is not JSON serializable")
+
class AuthServiceProxy(object):
+ __id_count = 0
+
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None):
self.__service_url = service_url
self.__service_name = service_name
@@ -66,7 +75,6 @@ class AuthServiceProxy(object):
port = 80
else:
port = self.__url.port
- self.__id_count = 0
(user, passwd) = (self.__url.username, self.__url.password)
try:
user = user.encode('utf8')
@@ -99,12 +107,14 @@ class AuthServiceProxy(object):
return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
def __call__(self, *args):
- self.__id_count += 1
+ AuthServiceProxy.__id_count += 1
+ log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self.__service_name,
+ json.dumps(args, default=EncodeDecimal)))
postdata = json.dumps({'version': '1.1',
'method': self.__service_name,
'params': args,
- 'id': self.__id_count})
+ 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal)
self.__conn.request('POST', self.__url.path, postdata,
{'Host': self.__url.hostname,
'User-Agent': USER_AGENT,
@@ -121,7 +131,8 @@ class AuthServiceProxy(object):
return response['result']
def _batch(self, rpc_call_list):
- postdata = json.dumps(list(rpc_call_list))
+ postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal)
+ log.debug("--> "+postdata)
self.__conn.request('POST', self.__url.path, postdata,
{'Host': self.__url.hostname,
'User-Agent': USER_AGENT,
@@ -136,5 +147,10 @@ class AuthServiceProxy(object):
raise JSONRPCException({
'code': -342, 'message': 'missing HTTP response from server'})
- return json.loads(http_response.read().decode('utf8'),
- parse_float=decimal.Decimal)
+ responsedata = http_response.read().decode('utf8')
+ response = json.loads(responsedata, parse_float=decimal.Decimal)
+ if "error" in response and response["error"] is None:
+ log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal)))
+ else:
+ log.debug("<-- "+responsedata)
+ return response
diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py
index 7f2d79b3c..61f5e0452 100755
--- a/qa/rpc-tests/receivedby.py
+++ b/qa/rpc-tests/receivedby.py
@@ -3,23 +3,13 @@
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-# Exercise the listtransactions API
-
-# Add python-bitcoinrpc to module search path:
-
-import os
-import sys
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
-
-import json
-import shutil
-import subprocess
-import tempfile
-import traceback
+# Exercise the listreceivedbyaddress API
+from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import *
+
def get_sub_array_from_array(object_array, to_match):
'''
Finds and returns a sub array from an array of arrays.
@@ -62,164 +52,115 @@ def check_array_result(object_array, to_match, expected, should_not_find = False
if num_matched > 0 and should_not_find == True:
raise AssertionError("Objects was matched %s"%(str(to_match)))
-def run_test(nodes):
- '''
- listreceivedbyaddress Test
- '''
- # Send from node 0 to 1
- addr = nodes[1].getnewaddress()
- txid = nodes[0].sendtoaddress(addr, 0.1)
- sync_mempools(nodes)
-
- #Check not listed in listreceivedbyaddress because has 0 confirmations
- check_array_result(nodes[1].listreceivedbyaddress(),
- {"address":addr},
- { },
- True)
- #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
- nodes[1].setgenerate(True, 10)
- sync_blocks(nodes)
- check_array_result(nodes[1].listreceivedbyaddress(),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence < 10
- check_array_result(nodes[1].listreceivedbyaddress(5),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence > 10, should not find Tx
- check_array_result(nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
-
- #Empty Tx
- addr = nodes[1].getnewaddress()
- check_array_result(nodes[1].listreceivedbyaddress(0,True),
- {"address":addr},
- {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
-
- '''
- getreceivedbyaddress Test
- '''
- # Send from node 0 to 1
- addr = nodes[1].getnewaddress()
- txid = nodes[0].sendtoaddress(addr, 0.1)
- sync_mempools(nodes)
-
- #Check balance is 0 because of 0 confirmations
- balance = nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
-
- #Check balance is 0.1
- balance = nodes[1].getreceivedbyaddress(addr,0)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
-
- #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
- nodes[1].setgenerate(True, 10)
- sync_blocks(nodes)
- balance = nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+class ReceivedByTest(BitcoinTestFramework):
- '''
- listreceivedbyaccount + getreceivedbyaccount Test
- '''
- #set pre-state
- addrArr = nodes[1].getnewaddress()
- account = nodes[1].getaccount(addrArr)
- received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(),{"account":account})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
- balance_by_account = rec_by_accountArr = nodes[1].getreceivedbyaccount(account)
-
- txid = nodes[0].sendtoaddress(addr, 0.1)
-
- # listreceivedbyaccount should return received_by_account_json because of 0 confirmations
- check_array_result(nodes[1].listreceivedbyaccount(),
- {"account":account},
- received_by_account_json)
-
- # getreceivedbyaddress should return same balance because of 0 confirmations
- balance = nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account:
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
-
- nodes[1].setgenerate(True, 10)
- sync_blocks(nodes)
- # listreceivedbyaccount should return updated account balance
- check_array_result(nodes[1].listreceivedbyaccount(),
- {"account":account},
- {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})
-
- # getreceivedbyaddress should return updates balance
- balance = nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account + Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
-
- #Create a new account named "mynewaccount" that has a 0 balance
- nodes[1].getaccountaddress("mynewaccount")
- received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
-
- # Test includeempty of listreceivedbyaccount
- if received_by_account_json["amount"] != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"]))
-
- # Test getreceivedbyaccount for 0 amount accounts
- balance = nodes[1].getreceivedbyaccount("mynewaccount")
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- nodes = start_nodes(2, options.tmpdir)
- connect_nodes(nodes[1], 0)
+ def run_test(self, nodes):
+ '''
+ listreceivedbyaddress Test
+ '''
+ # Send from node 0 to 1
+ addr = nodes[1].getnewaddress()
+ txid = nodes[0].sendtoaddress(addr, 0.1)
+ sync_mempools(nodes)
+
+ #Check not listed in listreceivedbyaddress because has 0 confirmations
+ check_array_result(nodes[1].listreceivedbyaddress(),
+ {"address":addr},
+ { },
+ True)
+ #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
+ nodes[1].setgenerate(True, 10)
sync_blocks(nodes)
-
- run_test(nodes)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(e))
- traceback.print_tb(sys.exc_info()[2])
-
- if not options.nocleanup:
- print("Cleaning up")
- stop_nodes(nodes)
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
-
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
+ check_array_result(nodes[1].listreceivedbyaddress(),
+ {"address":addr},
+ {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
+ #With min confidence < 10
+ check_array_result(nodes[1].listreceivedbyaddress(5),
+ {"address":addr},
+ {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
+ #With min confidence > 10, should not find Tx
+ check_array_result(nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
+
+ #Empty Tx
+ addr = nodes[1].getnewaddress()
+ check_array_result(nodes[1].listreceivedbyaddress(0,True),
+ {"address":addr},
+ {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
+
+ '''
+ getreceivedbyaddress Test
+ '''
+ # Send from node 0 to 1
+ addr = nodes[1].getnewaddress()
+ txid = nodes[0].sendtoaddress(addr, 0.1)
+ sync_mempools(nodes)
+
+ #Check balance is 0 because of 0 confirmations
+ balance = nodes[1].getreceivedbyaddress(addr)
+ if balance != Decimal("0.0"):
+ raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+
+ #Check balance is 0.1
+ balance = nodes[1].getreceivedbyaddress(addr,0)
+ if balance != Decimal("0.1"):
+ raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+
+ #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
+ nodes[1].setgenerate(True, 10)
+ sync_blocks(nodes)
+ balance = nodes[1].getreceivedbyaddress(addr)
+ if balance != Decimal("0.1"):
+ raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+
+ '''
+ listreceivedbyaccount + getreceivedbyaccount Test
+ '''
+ #set pre-state
+ addrArr = nodes[1].getnewaddress()
+ account = nodes[1].getaccount(addrArr)
+ received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(),{"account":account})
+ if len(received_by_account_json) == 0:
+ raise AssertionError("No accounts found in node")
+ balance_by_account = rec_by_accountArr = nodes[1].getreceivedbyaccount(account)
+
+ txid = nodes[0].sendtoaddress(addr, 0.1)
+
+ # listreceivedbyaccount should return received_by_account_json because of 0 confirmations
+ check_array_result(nodes[1].listreceivedbyaccount(),
+ {"account":account},
+ received_by_account_json)
+
+ # getreceivedbyaddress should return same balance because of 0 confirmations
+ balance = nodes[1].getreceivedbyaccount(account)
+ if balance != balance_by_account:
+ raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+
+ nodes[1].setgenerate(True, 10)
+ sync_blocks(nodes)
+ # listreceivedbyaccount should return updated account balance
+ check_array_result(nodes[1].listreceivedbyaccount(),
+ {"account":account},
+ {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})
+
+ # getreceivedbyaddress should return updates balance
+ balance = nodes[1].getreceivedbyaccount(account)
+ if balance != balance_by_account + Decimal("0.1"):
+ raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+
+ #Create a new account named "mynewaccount" that has a 0 balance
+ nodes[1].getaccountaddress("mynewaccount")
+ received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"})
+ if len(received_by_account_json) == 0:
+ raise AssertionError("No accounts found in node")
+
+ # Test includeempty of listreceivedbyaccount
+ if received_by_account_json["amount"] != Decimal("0.0"):
+ raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"]))
+
+ # Test getreceivedbyaccount for 0 amount accounts
+ balance = nodes[1].getreceivedbyaccount("mynewaccount")
+ if balance != Decimal("0.0"):
+ raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
if __name__ == '__main__':
- main()
+ ReceivedByTest().main()
diff --git a/qa/rpc-tests/skeleton.py b/qa/rpc-tests/skeleton.py
deleted file mode 100755
index 126b6bfaf..000000000
--- a/qa/rpc-tests/skeleton.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/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.
-
-# Skeleton for python-based regression tests using
-# JSON-RPC
-
-
-# Add python-bitcoinrpc to module search path:
-import os
-import sys
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
-
-import json
-import shutil
-import subprocess
-import tempfile
-import traceback
-
-from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
-from util import *
-
-
-def run_test(nodes):
- # Replace this as appropriate
- for node in nodes:
- assert_equal(node.getblockcount(), 200)
- assert_equal(node.getbalance(), 25*50)
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- nodes = start_nodes(2, options.tmpdir)
- connect_nodes(nodes[1], 0)
- sync_blocks(nodes)
-
- run_test(nodes)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(e))
- traceback.print_tb(sys.exc_info()[2])
-
- if not options.nocleanup:
- print("Cleaning up")
- stop_nodes(nodes)
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
-
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
-
-if __name__ == '__main__':
- main()
diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py
index e8abbfba1..352a1de2d 100755
--- a/qa/rpc-tests/smartfees.py
+++ b/qa/rpc-tests/smartfees.py
@@ -4,139 +4,86 @@
# Test fee estimation code
#
-# Add python-bitcoinrpc to module search path:
-import os
-import sys
-sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
-
-import json
-import random
-import shutil
-import subprocess
-import tempfile
-import traceback
-
+from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import *
+class EstimateFeeTest(BitcoinTestFramework):
-def run_test(nodes, test_dir):
- nodes.append(start_node(0, test_dir,
+ def setup_network(self, test_dir):
+ nodes = []
+ nodes.append(start_node(0, test_dir,
["-debug=mempool", "-debug=estimatefee"]))
- # Node1 mines small-but-not-tiny blocks, and allows free transactions.
- # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
- # so blockmaxsize of 2,000 is really just 1,000 bytes (room enough for
- # 6 or 7 transactions)
- nodes.append(start_node(1, test_dir,
- ["-blockprioritysize=1500", "-blockmaxsize=2000",
- "-debug=mempool", "-debug=estimatefee"]))
- connect_nodes(nodes[1], 0)
-
- # Node2 is a stingy miner, that
- # produces very small blocks (room for only 3 or so transactions)
- node2args = [ "-blockprioritysize=0", "-blockmaxsize=1500",
- "-debug=mempool", "-debug=estimatefee"]
- nodes.append(start_node(2, test_dir, node2args))
- connect_nodes(nodes[2], 0)
-
- sync_blocks(nodes)
-
- # Prime the memory pool with pairs of transactions
- # (high-priority, random fee and zero-priority, random fee)
- min_fee = Decimal("0.001")
- fees_per_kb = [];
- for i in range(12):
- (txid, txhex, fee) = random_zeropri_transaction(nodes, Decimal("1.1"),
- min_fee, min_fee, 20)
- tx_kbytes = (len(txhex)/2)/1000.0
- fees_per_kb.append(float(fee)/tx_kbytes)
-
- # Mine blocks with node2 until the memory pool clears:
- count_start = nodes[2].getblockcount()
- while len(nodes[2].getrawmempool()) > 0:
- nodes[2].setgenerate(True, 1)
- sync_blocks(nodes)
-
- all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
- print("Fee estimates, super-stingy miner: "+str([str(e) for e in all_estimates]))
-
- # Estimates should be within the bounds of what transactions fees actually were:
- delta = 1.0e-6 # account for rounding error
- for e in filter(lambda x: x >= 0, all_estimates):
- if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb):
- raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb))
+ # Node1 mines small-but-not-tiny blocks, and allows free transactions.
+ # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
+ # so blockmaxsize of 2,000 is really just 1,000 bytes (room enough for
+ # 6 or 7 transactions)
+ nodes.append(start_node(1, test_dir,
+ ["-blockprioritysize=1500", "-blockmaxsize=2000",
+ "-debug=mempool", "-debug=estimatefee"]))
+ connect_nodes(nodes[1], 0)
+
+ # Node2 is a stingy miner, that
+ # produces very small blocks (room for only 3 or so transactions)
+ node2args = [ "-blockprioritysize=0", "-blockmaxsize=1500",
+ "-debug=mempool", "-debug=estimatefee"]
+ nodes.append(start_node(2, test_dir, node2args))
+ connect_nodes(nodes[2], 0)
- # Generate transactions while mining 30 more blocks, this time with node1:
- for i in range(30):
- for j in range(random.randrange(6-4,6+4)):
- (txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"),
- Decimal("0.0"), min_fee, 20)
+ sync_blocks(nodes)
+ return nodes
+
+
+ def run_test(self, nodes):
+ # Prime the memory pool with pairs of transactions
+ # (high-priority, random fee and zero-priority, random fee)
+ min_fee = Decimal("0.001")
+ fees_per_kb = [];
+ for i in range(12):
+ (txid, txhex, fee) = random_zeropri_transaction(nodes, Decimal("1.1"),
+ min_fee, min_fee, 20)
tx_kbytes = (len(txhex)/2)/1000.0
fees_per_kb.append(float(fee)/tx_kbytes)
- nodes[1].setgenerate(True, 1)
- sync_blocks(nodes)
- all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
- print("Fee estimates, more generous miner: "+str([ str(e) for e in all_estimates]))
- for e in filter(lambda x: x >= 0, all_estimates):
- if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb):
- raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb))
-
- # Finish by mining a normal-sized block:
- while len(nodes[0].getrawmempool()) > 0:
- nodes[0].setgenerate(True, 1)
- sync_blocks(nodes)
+ # Mine blocks with node2 until the memory pool clears:
+ count_start = nodes[2].getblockcount()
+ while len(nodes[2].getrawmempool()) > 0:
+ nodes[2].setgenerate(True, 1)
+ sync_blocks(nodes)
+
+ all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
+ print("Fee estimates, super-stingy miner: "+str([str(e) for e in all_estimates]))
+
+ # Estimates should be within the bounds of what transactions fees actually were:
+ delta = 1.0e-6 # account for rounding error
+ for e in filter(lambda x: x >= 0, all_estimates):
+ if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb):
+ raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb))
+
+ # Generate transactions while mining 30 more blocks, this time with node1:
+ for i in range(30):
+ for j in range(random.randrange(6-4,6+4)):
+ (txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"),
+ Decimal("0.0"), min_fee, 20)
+ tx_kbytes = (len(txhex)/2)/1000.0
+ fees_per_kb.append(float(fee)/tx_kbytes)
+ nodes[1].setgenerate(True, 1)
+ sync_blocks(nodes)
+
+ all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
+ print("Fee estimates, more generous miner: "+str([ str(e) for e in all_estimates]))
+ for e in filter(lambda x: x >= 0, all_estimates):
+ if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb):
+ raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb))
+
+ # Finish by mining a normal-sized block:
+ while len(nodes[0].getrawmempool()) > 0:
+ nodes[0].setgenerate(True, 1)
+ sync_blocks(nodes)
+
+ final_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
+ print("Final fee estimates: "+str([ str(e) for e in final_estimates]))
- final_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ]
- print("Final fee estimates: "+str([ str(e) for e in final_estimates]))
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- print(" node0 running at: 127.0.0.1:%d"%(p2p_port(0)))
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- run_test(nodes, options.tmpdir)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(e))
- traceback.print_tb(sys.exc_info()[2])
-
- if not options.nocleanup:
- print("Cleaning up")
- stop_nodes(nodes)
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
-
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
if __name__ == '__main__':
- main()
+ EstimateFeeTest().main()
diff --git a/qa/rpc-tests/test_framework.py b/qa/rpc-tests/test_framework.py
new file mode 100755
index 000000000..5a1855665
--- /dev/null
+++ b/qa/rpc-tests/test_framework.py
@@ -0,0 +1,92 @@
+#!/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.
+
+# Base class for RPC testing
+
+# Add python-bitcoinrpc to module search path:
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
+
+import shutil
+import tempfile
+import traceback
+
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+
+class BitcoinTestFramework(object):
+
+ # These may be over-ridden by subclasses:
+ def run_test(self, nodes):
+ assert_equal(node.getblockcount(), 200)
+ assert_equal(node.getbalance(), 25*50)
+
+ def add_options(self, parser):
+ pass
+
+ def setup_chain(self, tmp_directory):
+ print("Initializing test directory "+tmp_directory)
+ initialize_chain(tmp_directory)
+
+ def setup_network(self, tmp_directory):
+ nodes = start_nodes(2, tmp_directory)
+ connect_nodes(nodes[1], 0)
+ sync_blocks(nodes)
+ return nodes
+
+ def main(self):
+ import optparse
+
+ parser = optparse.OptionParser(usage="%prog [options]")
+ parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
+ help="Leave bitcoinds and test.* datadir on exit or error")
+ parser.add_option("--srcdir", dest="srcdir", default="../../src",
+ help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
+ parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
+ help="Root directory for datadirs")
+ self.add_options(parser)
+ (self.options, self.args) = parser.parse_args()
+
+ os.environ['PATH'] = self.options.srcdir+":"+os.environ['PATH']
+
+ check_json_precision()
+
+ success = False
+ nodes = []
+ try:
+ if not os.path.isdir(self.options.tmpdir):
+ os.makedirs(self.options.tmpdir)
+ self.setup_chain(self.options.tmpdir)
+
+ nodes = self.setup_network(self.options.tmpdir)
+
+ self.run_test(nodes)
+
+ success = True
+
+ except JSONRPCException as e:
+ print("JSONRPC error: "+e.error['message'])
+ traceback.print_tb(sys.exc_info()[2])
+ except AssertionError as e:
+ print("Assertion failed: "+e.message)
+ traceback.print_tb(sys.exc_info()[2])
+ except Exception as e:
+ print("Unexpected exception caught during testing: "+str(e))
+ traceback.print_tb(sys.exc_info()[2])
+
+ if not self.options.nocleanup:
+ print("Cleaning up")
+ stop_nodes(nodes)
+ wait_bitcoinds()
+ shutil.rmtree(self.options.tmpdir)
+
+ if success:
+ print("Tests successful")
+ sys.exit(0)
+ else:
+ print("Failed")
+ sys.exit(1)
diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py
index 27c9f778f..0a7f26ffc 100644
--- a/qa/rpc-tests/util.py
+++ b/qa/rpc-tests/util.py
@@ -59,7 +59,7 @@ def sync_mempools(rpc_connections):
time.sleep(1)
-bitcoind_processes = []
+bitcoind_processes = {}
def initialize_datadir(dir, n):
datadir = os.path.join(dir, "node"+str(n))
@@ -88,7 +88,7 @@ def initialize_chain(test_dir):
args = [ "bitcoind", "-keypool=1", "-datadir="+datadir ]
if i > 0:
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
- bitcoind_processes.append(subprocess.Popen(args))
+ bitcoind_processes[i] = subprocess.Popen(args)
subprocess.check_call([ "bitcoin-cli", "-datadir="+datadir,
"-rpcwait", "getblockcount"], stdout=devnull)
devnull.close()
@@ -149,7 +149,7 @@ def start_node(i, dir, extra_args=None, rpchost=None):
datadir = os.path.join(dir, "node"+str(i))
args = [ "bitcoind", "-datadir="+datadir, "-keypool=1" ]
if extra_args is not None: args.extend(extra_args)
- bitcoind_processes.append(subprocess.Popen(args))
+ bitcoind_processes[i] = subprocess.Popen(args)
devnull = open("/dev/null", "w+")
subprocess.check_call([ "bitcoin-cli", "-datadir="+datadir] +
_rpchost_to_args(rpchost) +
@@ -168,6 +168,11 @@ def start_nodes(num_nodes, dir, extra_args=None, rpchost=None):
def debug_log(dir, n_node):
return os.path.join(dir, "node"+str(n_node), "regtest", "debug.log")
+def stop_node(node, i):
+ node.stop()
+ bitcoind_processes[i].wait()
+ del bitcoind_processes[i]
+
def stop_nodes(nodes):
for i in range(len(nodes)):
nodes[i].stop()
@@ -175,9 +180,9 @@ def stop_nodes(nodes):
def wait_bitcoinds():
# Wait for all bitcoinds to cleanly exit
- for bitcoind in bitcoind_processes:
+ for bitcoind in bitcoind_processes.values():
bitcoind.wait()
- del bitcoind_processes[:]
+ bitcoind_processes.clear()
def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:"+str(p2p_port(node_num))
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index e6afe3b48..d4bd58513 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -7,8 +7,9 @@ from subprocess import Popen, PIPE
import glob
import operator
import os
+import sys
-OUT_CPP="src/qt/bitcoinstrings.cpp"
+OUT_CPP="qt/bitcoinstrings.cpp"
EMPTY=['""']
def parse_po(text):
@@ -47,7 +48,7 @@ def parse_po(text):
return messages
-files = glob.glob('src/*.cpp') + glob.glob('src/*.h')
+files = sys.argv[1:]
# xgettext -n --keyword=_ $FILES
XGETTEXT=os.getenv('XGETTEXT', 'xgettext')
diff --git a/src/Makefile.am b/src/Makefile.am
index e2a62c969..9b0b97b7a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
AM_CPPFLAGS = $(INCLUDES)
-AM_LDFLAGS = $(PTHREAD_CFLAGS)
+AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS)
if USE_LIBSECP256K1
secp256k1/libsecp256k1.la: $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 4563bb356..75b7b683d 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -252,7 +252,10 @@ RES_ICONS = \
qt/res/icons/tx_inout.png \
qt/res/icons/tx_input.png \
qt/res/icons/tx_output.png \
- qt/res/icons/tx_mined.png
+ qt/res/icons/tx_mined.png \
+ qt/res/icons/unit_btc.png \
+ qt/res/icons/unit_mbtc.png \
+ qt/res/icons/unit_ubtc.png
BITCOIN_QT_CPP = \
qt/bitcoinaddressvalidator.cpp \
@@ -360,16 +363,16 @@ qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL)
if USE_LIBSECP256K1
qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la
endif
-qt_bitcoin_qt_LDFLAGS = $(QT_LDFLAGS)
+qt_bitcoin_qt_LDFLAGS = $(AM_LDFLAGS) $(QT_LDFLAGS)
#locale/foo.ts -> locale/foo.qm
QT_QM=$(QT_TS:.ts=.qm)
.SECONDARY: $(QT_QM)
-qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_cli_a_SOURCES) $(libbitcoin_util_a_SOURCES)
+qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES)
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
- $(AM_V_GEN) cd $(top_srcdir); XGETTEXT=$(XGETTEXT) share/qt/extract_strings_qt.py
+ $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) ../share/qt/extract_strings_qt.py $^
translate: qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 7e10ce5a9..51ce006fc 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -36,7 +36,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBIT
if USE_LIBSECP256K1
qt_test_test_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la
endif
-qt_test_test_bitcoin_qt_LDFLAGS = $(QT_LDFLAGS)
+qt_test_test_bitcoin_qt_LDFLAGS = $(AM_LDFLAGS) $(QT_LDFLAGS)
CLEAN_BITCOIN_QT_TEST = $(TEST_QT_MOC_CPP) qt/test/*.gcda qt/test/*.gcno
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index db39df4b1..016b2f50f 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -7,11 +7,12 @@
#include "init.h"
#include "rpcclient.h"
#include "rpcprotocol.h"
-#include "ui_interface.h" /* for _(...) */
#include "chainparamsbase.h"
#include <boost/filesystem/operations.hpp>
+#define _(x) std::string(x) /* Keep the _() around in case gettext or such will be used later to translate non-UI */
+
using namespace std;
using namespace boost;
using namespace boost::asio;
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 63067a153..b9097ea96 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -151,6 +151,7 @@ public:
vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org"));
vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com"));
vSeeds.push_back(CDNSSeedData("bitnodes.io", "seed.bitnodes.io"));
+ vSeeds.push_back(CDNSSeedData("open-nodes.org", "seeds.bitcoin.open-nodes.org"));
vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org"));
base58Prefixes[PUBKEY_ADDRESS] = list_of(0);
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 80479b47f..4cab11db3 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -127,7 +127,7 @@ namespace Checkpoints {
} else {
double nCheapBefore = data.nTransactionsLastCheckpoint;
double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint;
- double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay;
+ double nExpensiveAfter = (nNow - pindex->GetBlockTime())/86400.0*data.fTransactionsPerDay;
fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor;
fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor;
}
diff --git a/src/core.cpp b/src/core.cpp
index 47f3b2a01..b56994ecf 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -82,7 +82,12 @@ CFeeRate::CFeeRate(int64_t nFeePaid, size_t nSize)
int64_t CFeeRate::GetFee(size_t nSize) const
{
- return nSatoshisPerK*nSize / 1000;
+ int64_t nFee = nSatoshisPerK*nSize / 1000;
+
+ if (nFee == 0 && nSatoshisPerK > 0)
+ nFee = nSatoshisPerK;
+
+ return nFee;
}
std::string CFeeRate::ToString() const
diff --git a/src/init.cpp b/src/init.cpp
index 5afae3232..a1d75c967 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -38,8 +38,8 @@
#include <boost/interprocess/sync/file_lock.hpp>
#include <openssl/crypto.h>
-using namespace std;
using namespace boost;
+using namespace std;
#ifdef ENABLE_WALLET
CWallet* pwalletMain;
@@ -62,6 +62,7 @@ enum BindFlags {
};
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
+CClientUIInterface uiInterface;
//////////////////////////////////////////////////////////////////////////////
//
@@ -108,10 +109,11 @@ static CCoinsViewDB *pcoinsdbview;
void Shutdown()
{
- LogPrintf("Shutdown : In progress...\n");
+ LogPrintf("%s: In progress...\n", __func__);
static CCriticalSection cs_Shutdown;
TRY_LOCK(cs_Shutdown, lockShutdown);
- if (!lockShutdown) return;
+ if (!lockShutdown)
+ return;
RenameThread("bitcoin-shutoff");
mempool.AddTransactionsUpdated(1);
@@ -129,7 +131,7 @@ void Shutdown()
if (est_fileout)
mempool.WriteFeeEstimates(est_fileout);
else
- LogPrintf("failed to write fee estimates");
+ LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
{
LOCK(cs_main);
@@ -141,9 +143,12 @@ void Shutdown()
pblocktree->Flush();
if (pcoinsTip)
pcoinsTip->Flush();
- delete pcoinsTip; pcoinsTip = NULL;
- delete pcoinsdbview; pcoinsdbview = NULL;
- delete pblocktree; pblocktree = NULL;
+ delete pcoinsTip;
+ pcoinsTip = NULL;
+ delete pcoinsdbview;
+ pcoinsdbview = NULL;
+ delete pblocktree;
+ pblocktree = NULL;
}
#ifdef ENABLE_WALLET
if (pwalletMain)
@@ -155,7 +160,7 @@ void Shutdown()
if (pwalletMain)
delete pwalletMain;
#endif
- LogPrintf("Shutdown : done\n");
+ LogPrintf("%s: done\n", __func__);
}
//
@@ -238,9 +243,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n";
strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n";
strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n";
- strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS proxy") + "\n";
+ strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n";
strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n";
- strUsage += " -socks=<n> " + _("Select SOCKS version for -proxy (4 or 5, default: 5)") + "\n";
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
#ifdef USE_UPNP
#if USE_UPNP
@@ -253,7 +257,8 @@ std::string HelpMessage(HelpMessageMode mode)
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n";
- 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";
+ if (GetBoolArg("-help-debug", false))
+ strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n";
strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n";
strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + " " + _("on startup") + "\n";
strUsage += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\n";
@@ -290,6 +295,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n";
strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n";
+ strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n";
strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n";
if (GetBoolArg("-help-debug", false))
{
@@ -313,6 +319,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += "\n" + _("Node relay options:") + "\n";
strUsage += " -datacarrier " + _("Relay and mine data carrier transactions (default: 1)") + "\n";
+
strUsage += "\n" + _("Block creation options:") + "\n";
strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n";
strUsage += " -blockmaxsize=<n> " + strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE) + "\n";
@@ -562,9 +569,12 @@ bool AppInit2(boost::thread_group& threadGroup)
if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end())
fDebug = false;
- // Check for -debugnet (deprecated)
+ // Check for -debugnet
if (GetBoolArg("-debugnet", false))
- InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
+ InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net."));
+ // Check for -socks - as this is a privacy risk to continue, exit here
+ if (mapArgs.count("-socks"))
+ return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported."));
// Check for -tor - as this is a privacy risk to continue, exit here
if (GetBoolArg("-tor", false))
return InitError(_("Error: Unsupported argument -tor found, use -onion."));
@@ -586,6 +596,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fServer = GetBoolArg("-server", false);
fPrintToConsole = GetBoolArg("-printtoconsole", false);
fLogTimestamps = GetBoolArg("-logtimestamps", true);
+ fLogIPs = GetBoolArg("-logips", false);
setvbuf(stdout, NULL, _IOLBF, 0);
#ifdef ENABLE_WALLET
bool fDisableWallet = GetBoolArg("-disablewallet", false);
@@ -743,10 +754,6 @@ bool AppInit2(boost::thread_group& threadGroup)
RegisterNodeSignals(GetNodeSignals());
- int nSocksVersion = GetArg("-socks", 5);
- if (nSocksVersion != 4 && nSocksVersion != 5)
- return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion));
-
if (mapArgs.count("-onlynet")) {
std::set<enum Network> nets;
BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) {
@@ -770,12 +777,10 @@ bool AppInit2(boost::thread_group& threadGroup)
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"]));
if (!IsLimited(NET_IPV4))
- SetProxy(NET_IPV4, addrProxy, nSocksVersion);
- if (nSocksVersion > 4) {
- if (!IsLimited(NET_IPV6))
- SetProxy(NET_IPV6, addrProxy, nSocksVersion);
- SetNameProxy(addrProxy, nSocksVersion);
- }
+ SetProxy(NET_IPV4, addrProxy);
+ if (!IsLimited(NET_IPV6))
+ SetProxy(NET_IPV6, addrProxy);
+ SetNameProxy(addrProxy);
fProxy = true;
}
@@ -789,7 +794,7 @@ bool AppInit2(boost::thread_group& threadGroup)
addrOnion = CService(mapArgs["-onion"], 9050);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"]));
- SetProxy(NET_TOR, addrOnion, 5);
+ SetProxy(NET_TOR, addrOnion);
SetReachable(NET_TOR);
}
@@ -994,6 +999,7 @@ bool AppInit2(boost::thread_group& threadGroup)
boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein = CAutoFile(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION);
+ // Allowed to fail as this file IS missing on first startup.
if (est_filein)
mempool.ReadFeeEstimates(est_filein);
@@ -1183,7 +1189,7 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif
- RegisterInternalSignals();
+ InitRespendFilter();
StartNode(threadGroup);
if (fServer)
StartRPCThreads();
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 594e0c61d..2a4c88d56 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -59,3 +59,15 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
return false;
}
+bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
+{
+ LOCK(cs_KeyStore);
+ setWatchOnly.insert(dest);
+ return true;
+}
+
+bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const
+{
+ LOCK(cs_KeyStore);
+ return setWatchOnly.count(dest) > 0;
+}
diff --git a/src/keystore.h b/src/keystore.h
index 79d8661ac..72411a138 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -8,11 +8,21 @@
#include "key.h"
#include "sync.h"
+#include "script.h" // for CNoDestination
#include <boost/signals2/signal.hpp>
+#include <boost/variant.hpp>
class CScript;
+/** A txout script template with a specific destination. It is either:
+ * * CNoDestination: no destination set
+ * * CKeyID: TX_PUBKEYHASH destination
+ * * CScriptID: TX_SCRIPTHASH destination
+ * A CTxDestination is the internal data type encoded in a CBitcoinAddress
+ */
+typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
+
/** A virtual base class for key stores */
class CKeyStore
{
@@ -36,10 +46,15 @@ public:
virtual bool AddCScript(const CScript& redeemScript) =0;
virtual bool HaveCScript(const CScriptID &hash) const =0;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
+
+ // Support for Watch-only addresses
+ virtual bool AddWatchOnly(const CScript &dest) =0;
+ virtual bool HaveWatchOnly(const CScript &dest) const =0;
};
typedef std::map<CKeyID, CKey> KeyMap;
typedef std::map<CScriptID, CScript > ScriptMap;
+typedef std::set<CScript> WatchOnlySet;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
@@ -47,6 +62,7 @@ class CBasicKeyStore : public CKeyStore
protected:
KeyMap mapKeys;
ScriptMap mapScripts;
+ WatchOnlySet setWatchOnly;
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
@@ -88,6 +104,9 @@ public:
virtual bool AddCScript(const CScript& redeemScript);
virtual bool HaveCScript(const CScriptID &hash) const;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
+
+ virtual bool AddWatchOnly(const CScript &dest);
+ virtual bool HaveWatchOnly(const CScript &dest) const;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
diff --git a/src/main.cpp b/src/main.cpp
index 54b926abd..a9c080ffa 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -123,9 +123,14 @@ namespace {
} // anon namespace
-// Forward reference functions defined here:
+// Bloom filter to limit respend relays to one
static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
-static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter);
+static CBloomFilter doubleSpendFilter;
+void InitRespendFilter() {
+ seed_insecure_rand();
+ doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
+}
+
//////////////////////////////////////////////////////////////////////////////
//
@@ -149,24 +154,10 @@ struct CMainSignals {
boost::signals2::signal<void (const uint256 &)> Inventory;
// Tells listeners to broadcast their data.
boost::signals2::signal<void ()> Broadcast;
- // Notifies listeners of detection of a double-spent transaction. Arguments are outpoint that is
- // double-spent, first transaction seen, double-spend transaction, and whether the second double-spend
- // transaction was first seen in a block.
- // Note: only notifies if the previous transaction is in the memory pool; if previous transction was in a block,
- // then the double-spend simply fails when we try to lookup the inputs in the current UTXO set.
- boost::signals2::signal<void (const COutPoint&, const CTransaction&, bool)> DetectedDoubleSpend;
} g_signals;
} // anon namespace
-void RegisterInternalSignals() {
- static CBloomFilter doubleSpendFilter;
- seed_insecure_rand();
- doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
-
- g_signals.DetectedDoubleSpend.connect(boost::bind(RelayDoubleSpend, _1, _2, _3, doubleSpendFilter));
-}
-
void RegisterWallet(CWalletInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
@@ -432,15 +423,13 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
break;
// Exponentially larger steps back, plus the genesis block.
int nHeight = std::max(pindex->nHeight - nStep, 0);
- // Jump back quickly to the same height as the chain.
- if (pindex->nHeight > nHeight)
- pindex = pindex->GetAncestor(nHeight);
- // In case pindex is not in this chain, iterate pindex->pprev to find blocks.
- while (!Contains(pindex))
- pindex = pindex->pprev;
- // If pindex is in this chain, use direct height-based access.
- if (pindex->nHeight > nHeight)
+ if (Contains(pindex)) {
+ // Use O(1) CChain index if possible.
pindex = (*this)[nHeight];
+ } else {
+ // Otherwise, use O(log n) skiplist.
+ pindex = pindex->GetAncestor(nHeight);
+ }
if (vHave.size() > 10)
nStep *= 2;
}
@@ -901,6 +890,45 @@ bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsig
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)
{
@@ -929,6 +957,7 @@ 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++)
@@ -937,8 +966,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// 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))
{
- g_signals.DetectedDoubleSpend(outpoint, tx, false);
- return false;
+ relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter);
+ if (!relayableRespend)
+ return false;
}
}
}
@@ -1031,55 +1061,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
{
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
}
- // Store transaction in memory
- pool.addUnchecked(hash, entry);
- }
-
- g_signals.SyncTransaction(tx, NULL);
-
- return true;
-}
-
-static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
-{
- // Relaying double-spend attempts to our peers lets them detect when
- // somebody might be trying to cheat them. However, blindly relaying
- // every double-spend across the entire network gives attackers
- // a denial-of-service attack: just generate a stream of double-spends
- // re-spending the same (limited) set of outpoints owned by the attacker.
- // So, we use a bloom filter and only relay (at most) the first double
- // spend for each outpoint. False-positives ("we have already relayed")
- // are OK, because if the peer doesn't hear about the double-spend
- // from us they are very likely to hear about it from another peer, since
- // each peer uses a different, randomized bloom filter.
-
- if (fInBlock || filter.contains(outPoint)) return;
-
- // Apply an independent rate limit to double-spend relays
- static double dRespendCount;
- static int64_t nLastRespendTime;
- static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
- unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
- if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
- {
- LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
- return;
+ if (relayableRespend)
+ {
+ RelayTransaction(tx);
+ }
+ else
+ {
+ // Store transaction in memory
+ pool.addUnchecked(hash, entry);
+ }
}
- LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
-
- // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
- // insertions
- if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
- filter.clear();
-
- filter.insert(outPoint);
-
- RelayTransaction(doubleSpend);
+ g_signals.SyncTransaction(tx, NULL);
- // Share conflict with wallet
- g_signals.SyncTransaction(doubleSpend, NULL);
+ return !relayableRespend;
}
@@ -1790,7 +1786,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
// BIP16 didn't become active until Apr 1 2012
int64_t nBIP16SwitchTime = 1333238400;
- bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
+ bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
unsigned int flags = SCRIPT_VERIFY_NOCACHE |
(fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE);
@@ -2439,7 +2435,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex
if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0)))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
- int64_t deltaTime = block.GetBlockTime() - pcheckpoint->nTime;
+ int64_t deltaTime = block.GetBlockTime() - pcheckpoint->GetBlockTime();
if (deltaTime < 0)
{
return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"),
@@ -2550,7 +2546,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
CDiskBlockPos blockPos;
if (dbp != NULL)
blockPos = *dbp;
- if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL))
+ if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL))
return error("AcceptBlock() : FindBlockPos failed");
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos))
@@ -3148,7 +3144,7 @@ bool InitBlockIndex() {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
CValidationState state;
- if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime))
+ if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime()))
return error("LoadBlockIndex() : FindBlockPos failed");
if (!WriteBlockToDisk(block, blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
@@ -3537,10 +3533,10 @@ void static ProcessGetData(CNode* pfrom)
}
}
-bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
+bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{
RandAddSeedPerfmon();
- LogPrint("net", "received: %s (%u bytes)\n", strCommand, vRecv.size());
+ LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
@@ -3572,7 +3568,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
- LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion);
+ LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
@@ -3653,7 +3649,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->fSuccessfullyConnected = true;
- LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString());
+ LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id);
AddTimeData(pfrom->addr, nTime);
}
@@ -3760,7 +3756,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(inv);
- LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new");
+ LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
if (!fAlreadyHave) {
if (!fImporting && !fReindex) {
@@ -3793,10 +3789,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
if (fDebug || (vInv.size() != 1))
- LogPrint("net", "received getdata (%u invsz)\n", vInv.size());
+ LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
- LogPrint("net", "received getdata for: %s\n", vInv[0].ToString());
+ LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom);
@@ -3818,7 +3814,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (pindex)
pindex = chainActive.Next(pindex);
int nLimit = 500;
- LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit);
+ LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id);
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -3901,8 +3897,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vEraseQueue.push_back(inv.hash);
- LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %u)\n",
- pfrom->addr.ToString(), pfrom->cleanSubVer,
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n",
+ pfrom->id, pfrom->cleanSubVer,
tx.GetHash().ToString(),
mempool.mapTx.size());
@@ -3955,8 +3951,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
int nDoS = 0;
if (state.IsInvalid(nDoS))
{
- LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
- pfrom->addr.ToString(), pfrom->cleanSubVer,
+ LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
+ pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason());
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason(), inv.hash);
@@ -3971,7 +3967,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CBlock block;
vRecv >> block;
- LogPrint("net", "received block %s\n", block.GetHash().ToString());
+ LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id);
// block.print();
CInv inv(MSG_BLOCK, block.GetHash());
@@ -3986,6 +3982,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CValidationState state;
ProcessBlock(state, pfrom, &block);
+ int nDoS;
+ if (state.IsInvalid(nDoS)) {
+ pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
+ state.GetRejectReason(), inv.hash);
+ if (nDoS > 0) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), nDoS);
+ }
+ }
+
}
@@ -4047,7 +4053,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
else if (strCommand == "pong")
{
- int64_t pingUsecEnd = GetTimeMicros();
+ int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
@@ -4088,8 +4094,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
if (!(sProblem.empty())) {
- LogPrint("net", "pong %s %s: %s, %x expected, %x received, %u bytes\n",
- pfrom->addr.ToString(),
+ LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n",
+ pfrom->id,
pfrom->cleanSubVer,
sProblem,
pfrom->nPingNonceSent,
@@ -4298,7 +4304,7 @@ bool ProcessMessages(CNode* pfrom)
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime);
boost::this_thread::interruption_point();
}
catch (std::ios_base::failure& e)
@@ -4329,7 +4335,7 @@ bool ProcessMessages(CNode* pfrom)
}
if (!fRet)
- LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand, nMessageSize);
+ LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id);
break;
}
@@ -4533,7 +4539,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
uint256 hash = state.vBlocksToDownload.front();
vGetData.push_back(CInv(MSG_BLOCK, hash));
MarkBlockAsInFlight(pto->GetId(), hash);
- LogPrint("net", "Requesting block %s from %s\n", hash.ToString(), state.name);
+ LogPrint("net", "Requesting block %s peer=%d\n", hash.ToString(), pto->id);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
@@ -4550,7 +4556,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if (!AlreadyHave(inv))
{
if (fDebug)
- LogPrint("net", "sending getdata: %s\n", inv.ToString());
+ LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
diff --git a/src/main.h b/src/main.h
index 961f2e78a..f6bac889b 100644
--- a/src/main.h
+++ b/src/main.h
@@ -109,8 +109,8 @@ struct CNodeStateStats;
struct CBlockTemplate;
-/** Set up internal signal handlers **/
-void RegisterInternalSignals();
+/** Initialize respend bloom filter **/
+void InitRespendFilter();
/** Register a wallet to receive updates from core */
void RegisterWallet(CWalletInterface* pwalletIn);
@@ -712,7 +712,7 @@ public:
// (memory only) Sequencial id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;
- CBlockIndex()
+ void SetNull()
{
phashBlock = NULL;
pprev = NULL;
@@ -734,20 +734,14 @@ public:
nNonce = 0;
}
+ CBlockIndex()
+ {
+ SetNull();
+ }
+
CBlockIndex(CBlockHeader& block)
{
- phashBlock = NULL;
- pprev = NULL;
- pskip = NULL;
- nHeight = 0;
- nFile = 0;
- nDataPos = 0;
- nUndoPos = 0;
- nChainWork = 0;
- nTx = 0;
- nChainTx = 0;
- nStatus = 0;
- nSequenceId = 0;
+ SetNull();
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
diff --git a/src/net.cpp b/src/net.cpp
index 934c45ca4..6a660dc9b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -491,8 +491,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
{
addrman.Attempt(addrConnect);
- LogPrint("net", "connected %s\n", pszDest ? pszDest : addrConnect.ToString());
-
// Set to non-blocking
#ifdef WIN32
u_long nOne = 1;
@@ -513,6 +511,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
}
pnode->nTimeConnected = GetTime();
+
return pnode;
}
@@ -524,7 +523,7 @@ void CNode::CloseSocketDisconnect()
fDisconnect = true;
if (hSocket != INVALID_SOCKET)
{
- LogPrint("net", "disconnecting node %s\n", addrName);
+ LogPrint("net", "disconnecting peer=%d\n", id);
closesocket(hSocket);
hSocket = INVALID_SOCKET;
}
@@ -548,7 +547,10 @@ void CNode::PushVersion()
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
CAddress addrMe = GetLocalAddress(&addr);
RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString());
+ if (fLogIPs)
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
+ else
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
}
@@ -653,6 +655,9 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
pch += handled;
nBytes -= handled;
+
+ if (msg.complete())
+ msg.nTime = GetTimeMicros();
}
return true;
@@ -950,7 +955,6 @@ void ThreadSocketHandler()
}
else
{
- LogPrint("net", "accepted connection %s\n", addr.ToString());
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
@@ -1040,7 +1044,7 @@ void ThreadSocketHandler()
{
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
- LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
+ LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
diff --git a/src/net.h b/src/net.h
index 2ee798d46..c2a041645 100644
--- a/src/net.h
+++ b/src/net.h
@@ -173,11 +173,14 @@ public:
CDataStream vRecv; // received message data
unsigned int nDataPos;
+ int64_t nTime; // time (in microseconds) of message receipt.
+
CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) {
hdrbuf.resize(24);
in_data = false;
nHdrPos = 0;
nDataPos = 0;
+ nTime = 0;
}
bool complete() const
@@ -330,6 +333,11 @@ public:
id = nLastNodeId++;
}
+ if (fLogIPs)
+ LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
+ else
+ LogPrint("net", "Added connection peer=%d\n", id);
+
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
PushVersion();
@@ -446,7 +454,7 @@ public:
nRequestTime = it->second;
else
nRequestTime = 0;
- LogPrint("net", "askfor %s %d (%s)\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str());
+ LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id);
// Make sure not to reuse time indexes to keep things in the same order
int64_t nNow = GetTimeMicros() - 1000000;
@@ -514,7 +522,7 @@ public:
assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum));
memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum));
- LogPrint("net", "(%d bytes)\n", nSize);
+ LogPrint("net", "(%d bytes) peer=%d\n", nSize, id);
std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
ssSend.GetAndClear(*it);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 3c50174e7..067cfa024 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -3,6 +3,14 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include "bitcoin-config.h"
+#endif
+
+#ifdef HAVE_GETADDRINFO_A
+#include <netdb.h>
+#endif
+
#include "netbase.h"
#include "hash.h"
@@ -11,6 +19,9 @@
#include "util.h"
#ifndef WIN32
+#if HAVE_INET_PTON
+#include <arpa/inet.h>
+#endif
#include <fcntl.h>
#endif
@@ -25,7 +36,7 @@ using namespace std;
// Settings
static proxyType proxyInfo[NET_MAX];
-static proxyType nameproxyInfo;
+static CService nameProxy;
static CCriticalSection cs_proxyInfos;
int nConnectTimeout = 5000;
bool fNameLookup = false;
@@ -71,9 +82,30 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
}
}
+#ifdef HAVE_GETADDRINFO_A
+ struct in_addr ipv4_addr;
+#ifdef HAVE_INET_PTON
+ if (inet_pton(AF_INET, pszName, &ipv4_addr) > 0) {
+ vIP.push_back(CNetAddr(ipv4_addr));
+ return true;
+ }
+
+ struct in6_addr ipv6_addr;
+ if (inet_pton(AF_INET6, pszName, &ipv6_addr) > 0) {
+ vIP.push_back(CNetAddr(ipv6_addr));
+ return true;
+ }
+#else
+ ipv4_addr.s_addr = inet_addr(pszName);
+ if (ipv4_addr.s_addr != INADDR_NONE) {
+ vIP.push_back(CNetAddr(ipv4_addr));
+ return true;
+ }
+#endif
+#endif
+
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
-
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
aiHint.ai_family = AF_UNSPEC;
@@ -82,8 +114,33 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
#else
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
+
struct addrinfo *aiRes = NULL;
+#ifdef HAVE_GETADDRINFO_A
+ struct gaicb gcb, *query = &gcb;
+ memset(query, 0, sizeof(struct gaicb));
+ gcb.ar_name = pszName;
+ gcb.ar_request = &aiHint;
+ int nErr = getaddrinfo_a(GAI_NOWAIT, &query, 1, NULL);
+ if (nErr)
+ return false;
+
+ do {
+ // Should set the timeout limit to a resonable value to avoid
+ // generating unnecessary checking call during the polling loop,
+ // while it can still response to stop request quick enough.
+ // 2 seconds looks fine in our situation.
+ struct timespec ts = { 2, 0 };
+ gai_suspend(&query, 1, &ts);
+ boost::this_thread::interruption_point();
+
+ nErr = gai_error(query);
+ if (0 == nErr)
+ aiRes = query->ar_result;
+ } while (nErr == EAI_INPROGRESS);
+#else
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
+#endif
if (nErr)
return false;
@@ -156,50 +213,6 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
return Lookup(pszName, addr, portDefault, false);
}
-bool static Socks4(const CService &addrDest, SOCKET& hSocket)
-{
- LogPrintf("SOCKS4 connecting %s\n", addrDest.ToString());
- if (!addrDest.IsIPv4())
- {
- closesocket(hSocket);
- return error("Proxy destination is not IPv4");
- }
- char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user";
- struct sockaddr_in addr;
- socklen_t len = sizeof(addr);
- if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET)
- {
- closesocket(hSocket);
- return error("Cannot get proxy destination address");
- }
- memcpy(pszSocks4IP + 2, &addr.sin_port, 2);
- memcpy(pszSocks4IP + 4, &addr.sin_addr, 4);
- char* pszSocks4 = pszSocks4IP;
- int nSize = sizeof(pszSocks4IP);
-
- int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL);
- if (ret != nSize)
- {
- closesocket(hSocket);
- return error("Error sending to proxy");
- }
- char pchRet[8];
- if (recv(hSocket, pchRet, 8, 0) != 8)
- {
- closesocket(hSocket);
- return error("Error reading proxy response");
- }
- if (pchRet[1] != 0x5a)
- {
- closesocket(hSocket);
- if (pchRet[1] != 0x5b)
- LogPrintf("ERROR: Proxy returned error %d\n", pchRet[1]);
- return false;
- }
- LogPrintf("SOCKS4 connected %s\n", addrDest.ToString());
- return true;
-}
-
bool static Socks5(string strDest, int port, SOCKET& hSocket)
{
LogPrintf("SOCKS5 connecting %s\n", strDest);
@@ -410,53 +423,49 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
return true;
}
-bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) {
+bool SetProxy(enum Network net, CService addrProxy) {
assert(net >= 0 && net < NET_MAX);
- if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5)
- return false;
- if (nSocksVersion != 0 && !addrProxy.IsValid())
+ if (!addrProxy.IsValid())
return false;
LOCK(cs_proxyInfos);
- proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion);
+ proxyInfo[net] = addrProxy;
return true;
}
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
assert(net >= 0 && net < NET_MAX);
LOCK(cs_proxyInfos);
- if (!proxyInfo[net].second)
+ if (!proxyInfo[net].IsValid())
return false;
proxyInfoOut = proxyInfo[net];
return true;
}
-bool SetNameProxy(CService addrProxy, int nSocksVersion) {
- if (nSocksVersion != 0 && nSocksVersion != 5)
- return false;
- if (nSocksVersion != 0 && !addrProxy.IsValid())
+bool SetNameProxy(CService addrProxy) {
+ if (!addrProxy.IsValid())
return false;
LOCK(cs_proxyInfos);
- nameproxyInfo = std::make_pair(addrProxy, nSocksVersion);
+ nameProxy = addrProxy;
return true;
}
-bool GetNameProxy(proxyType &nameproxyInfoOut) {
+bool GetNameProxy(CService &nameProxyOut) {
LOCK(cs_proxyInfos);
- if (!nameproxyInfo.second)
+ if(!nameProxy.IsValid())
return false;
- nameproxyInfoOut = nameproxyInfo;
+ nameProxyOut = nameProxy;
return true;
}
bool HaveNameProxy() {
LOCK(cs_proxyInfos);
- return nameproxyInfo.second != 0;
+ return nameProxy.IsValid();
}
bool IsProxy(const CNetAddr &addr) {
LOCK(cs_proxyInfos);
for (int i = 0; i < NET_MAX; i++) {
- if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first))
+ if (addr == (CNetAddr)proxyInfo[i])
return true;
}
return false;
@@ -465,31 +474,18 @@ bool IsProxy(const CNetAddr &addr) {
bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout)
{
proxyType proxy;
-
- // no proxy needed
+ // no proxy needed (none set for target network)
if (!GetProxy(addrDest.GetNetwork(), proxy))
return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
SOCKET hSocket = INVALID_SOCKET;
// first connect to proxy server
- if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout))
+ if (!ConnectSocketDirectly(proxy, hSocket, nTimeout))
return false;
-
// do socks negotiation
- switch (proxy.second) {
- case 4:
- if (!Socks4(addrDest, hSocket))
- return false;
- break;
- case 5:
- if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket))
- return false;
- break;
- default:
- closesocket(hSocket);
+ if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket))
return false;
- }
hSocketRet = hSocket;
return true;
@@ -503,30 +499,25 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest
SOCKET hSocket = INVALID_SOCKET;
- proxyType nameproxy;
- GetNameProxy(nameproxy);
+ CService nameProxy;
+ GetNameProxy(nameProxy);
- CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port);
+ CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port);
if (addrResolved.IsValid()) {
addr = addrResolved;
return ConnectSocket(addr, hSocketRet, nTimeout);
}
+
addr = CService("0.0.0.0:0");
- if (!nameproxy.second)
+
+ if (!HaveNameProxy())
return false;
- if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout))
+ // first connect to name proxy server
+ if (!ConnectSocketDirectly(nameProxy, hSocket, nTimeout))
+ return false;
+ // do socks negotiation
+ if (!Socks5(strDest, (unsigned short)port, hSocket))
return false;
-
- switch(nameproxy.second) {
- default:
- case 4:
- closesocket(hSocket);
- return false;
- case 5:
- if (!Socks5(strDest, port, hSocket))
- return false;
- break;
- }
hSocketRet = hSocket;
return true;
@@ -1261,7 +1252,8 @@ std::string NetworkErrorString(int err)
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
s = strerror_r(err, buf, sizeof(buf));
#else /* POSIX variant always returns message in buffer */
- (void) strerror_r(err, buf, sizeof(buf));
+ if (strerror_r(err, buf, sizeof(buf)))
+ buf[0] = 0;
#endif
return strprintf("%s (%d)", s, err);
}
diff --git a/src/netbase.h b/src/netbase.h
index 40a3d2567..ad1e23083 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -161,14 +161,14 @@ class CService : public CNetAddr
)
};
-typedef std::pair<CService, int> proxyType;
+typedef CService proxyType;
enum Network ParseNetwork(std::string net);
void SplitHostPort(std::string in, int &portOut, std::string &hostOut);
-bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5);
+bool SetProxy(enum Network net, CService addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
-bool SetNameProxy(CService addrProxy, int nSocksVersion = 5);
+bool SetNameProxy(CService addrProxy);
bool HaveNameProxy();
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true);
bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true);
diff --git a/src/pow.cpp b/src/pow.cpp
index 952250dec..c0d0a7ca2 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -26,7 +26,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes
// then allow mining of a min-difficulty block.
- if (pblock->nTime > pindexLast->nTime + Params().TargetSpacing()*2)
+ if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + Params().TargetSpacing()*2)
return nProofOfWorkLimit;
else
{
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index f38200c7f..357c6470d 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -35,6 +35,9 @@
<file alias="tx_input">res/icons/tx_input.png</file>
<file alias="tx_output">res/icons/tx_output.png</file>
<file alias="tx_inout">res/icons/tx_inout.png</file>
+ <file alias="unit_btc">res/icons/unit_btc.png</file>
+ <file alias="unit_mbtc">res/icons/unit_mbtc.png</file>
+ <file alias="unit_ubtc">res/icons/unit_ubtc.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
<file alias="key">res/icons/key.png</file>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index f2fb8c877..6b3aa2a2d 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -34,13 +34,10 @@
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QIcon>
-#include <QLabel>
#include <QListWidget>
-#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
-#include <QPoint>
#include <QProgressBar>
#include <QProgressDialog>
#include <QSettings>
@@ -51,8 +48,6 @@
#include <QToolBar>
#include <QVBoxLayout>
-
-
#if QT_VERSION < 0x050000
#include <QUrl>
#include <QTextDocument>
@@ -1014,7 +1009,6 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
{
optionsModel = 0;
createContextMenu();
- setStyleSheet("font:11pt; color: #333333");
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
}
@@ -1059,7 +1053,7 @@ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel)
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
{
- setText(BitcoinUnits::name(newUnits));
+ setPixmap(QIcon(":/icons/unit_" + BitcoinUnits::id(newUnits)).pixmap(31,STATUSBAR_ICONSIZE));
}
/** Shows context menu with Display Unit options by the mouse coordinates */
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 705e629a6..30dd7ae31 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -21,18 +21,14 @@ class Notificator;
class OptionsModel;
class RPCConsole;
class SendCoinsRecipient;
+class UnitDisplayStatusBarControl;
class WalletFrame;
class WalletModel;
class CWallet;
-class UnitDisplayStatusBarControl;
-
QT_BEGIN_NAMESPACE
class QAction;
-class QLabel;
-class QMenu;
-class QPoint;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 10b44bbc3..e852c468a 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -22,31 +22,42 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"It is also recommended to set alertnotify so you are notified of problems;\n"
"for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" [email protected]\n"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"(default: 1, 1 = keep tx meta data e.g. account owner and payment request "
+"information, 2 = drop tx meta data)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!"
"3DES:@STRENGTH)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"An error occurred while setting up the RPC port %u for listening on IPv4: %s"),
+"Allow JSON-RPC connections from specified source. Valid for <ip> are a "
+"single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or "
+"a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"An error occurred while setting up the RPC port %u for listening on IPv6, "
-"falling back to IPv4: %s"),
+"An error occurred while setting up the RPC address %s port %u for listening: "
+"%s"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Bind to given address and always listen on it. Use [host]:port notation for "
"IPv6"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Bind to given address to listen for JSON-RPC connections. Use [host]:port "
+"notation for IPv6. This option can be specified multiple times (default: "
+"bind to all interfaces)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. Bitcoin Core is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Continuously rate-limit free transactions to <n>*1000 bytes per minute "
"(default:15)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Enter regression test mode, which uses a special chain in which blocks can "
-"be solved instantly. This is intended for regression testing tools and app "
-"development."),
+"Delete all wallet transactions and only recover those part of the blockchain "
+"through -rescan on startup"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Distributed under the MIT/X11 software license, see the accompanying file "
+"COPYING or <http://www.opensource.org/licenses/mit-license.php>."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Enter regression test mode, which uses a special chain in which blocks can "
"be solved instantly."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Error: Listening for incoming connections failed (listen returned error %d)"),
+"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: The transaction was rejected! This might happen if some of the coins "
"in your wallet were already spent, such as if you used a copy of wallet.dat "
@@ -55,6 +66,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: This transaction requires a transaction fee of at least %s because of "
"its amount, complexity, or use of recently received funds!"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Execute command when a network tx respends wallet tx input (%s=respend TxID, "
+"%t=wallet TxID)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Execute command when a relevant alert is received or we see a really long "
"fork (%s in cmd is replaced by message)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -64,14 +78,20 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Execute command when the best block changes (%s in cmd is replaced by block "
"hash)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Fees smaller than this are considered zero fee (for transaction creation) "
-"(default:"),
+"Fees (in BTC/Kb) smaller than this are considered zero fee for relaying "
+"(default: %s)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Fees (in BTC/Kb) smaller than this are considered zero fee for transaction "
+"creation (default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Flush database activity from memory pool to disk log every <n> megabytes "
"(default: 100)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"How thorough the block verification of -checkblocks is (0-4, default: 3)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"If paytxfee is not set, include enough fee so transactions are confirmed on "
+"average within n blocks (default: 1)"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"In this mode -genproclimit controls how many blocks are generated "
"immediately."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -93,6 +113,10 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"This is a pre-release test build - use at your own risk - do not use for "
"mining or merchant applications"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"This product includes software developed by the OpenSSL Project for use in "
+"the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software "
+"written by Eric Young and UPnP software written by Thomas Bernard."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to bind to %s on this computer. Bitcoin Core is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -117,11 +141,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as "
"wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect "
"you should restore from a backup."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"You must set rpcpassword=<password> in the configuration file:\n"
-"%s\n"
-"If the file does not exist, create it with owner-readable-only file "
-"permissions."),
QT_TRANSLATE_NOOP("bitcoin-core", "(default: 1)"),
QT_TRANSLATE_NOOP("bitcoin-core", "(default: wallet.dat)"),
QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"),
@@ -129,22 +148,19 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"),
QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"),
QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core Daemon"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core RPC client version"),
QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Clear list of wallet transactions (diagnostic tool; implies -rescan)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connect through SOCKS proxy"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connection options:"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) 2009-%i The Bitcoin Core Developers"),
QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Could not parse -rpcbind value %s as network address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Debugging/Testing options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Disable safemode, override a real safe mode event (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"),
@@ -160,6 +176,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires new
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unsupported argument -tor found, use -onion."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
@@ -173,27 +190,28 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Fee per kB to add to transactions you send"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Fees smaller than this are considered zero fee (for relaying) (default:"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in BTC/kB) to add to transactions you send (default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Force safe mode (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: 0)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"),
QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"),
QT_TRANSLATE_NOOP("bitcoin-core", "If <category> is not supplied, output all debugging information."),
QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Include IP addresses in debug output (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. Bitcoin Core is shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=<amount>: '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable blocks in memory (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Limit size of signature cache to <n> entries (default: 50000)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "List commands"),
QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: 8333 or testnet: 18333)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
@@ -203,6 +221,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Maintain a full transaction index (default: 0
QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most <n> connections to peers (default: 125)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Node relay options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."),
QT_TRANSLATE_NOOP("bitcoin-core", "Only accept block chain matching built-in checkpoints (default: 1)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (IPv4, IPv6 or Tor)"),
@@ -212,19 +231,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default:
QT_TRANSLATE_NOOP("bitcoin-core", "Print block on startup, if found in block index"),
QT_TRANSLATE_NOOP("bitcoin-core", "Print block tree on startup (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "RPC client options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "RPC server options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Randomly drop 1 of every <n> network messages"),
QT_TRANSLATE_NOOP("bitcoin-core", "Randomly fuzz 1 of every <n> network messages"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: 1)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Run a thread to flush wallet periodically (default: 1)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"),
-QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the Bitcoin Wiki for SSL setup instructions)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Select SOCKS version for -proxy (4 or 5, default: 5)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Send command to Bitcoin Core"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on <ip> (default: 127.0.0.1)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"),
QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)"),
@@ -245,21 +261,20 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: 1)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Start Bitcoin Core Daemon"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Stop running after importing blocks from disk (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "System error: "),
QT_TRANSLATE_NOOP("bitcoin-core", "This help message"),
+QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."),
QT_TRANSLATE_NOOP("bitcoin-core", "This is intended for regression testing tools and app development."),
QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"),
QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must be positive"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %d, %s)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Usage (deprecated, use bitcoin-cli):"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 0)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 1 when listening)"),
@@ -267,7 +282,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"),
QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Wait for RPC server to start"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet options:"),
@@ -277,6 +291,5 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade re
QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"),
QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."),
QT_TRANSLATE_NOOP("bitcoin-core", "on startup"),
-QT_TRANSLATE_NOOP("bitcoin-core", "version"),
QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"),
};
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 4ba6aba55..bbc9b2e5a 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -34,6 +34,17 @@ bool BitcoinUnits::valid(int unit)
}
}
+QString BitcoinUnits::id(int unit)
+{
+ switch(unit)
+ {
+ case BTC: return QString("btc");
+ case mBTC: return QString("mbtc");
+ case uBTC: return QString("ubtc");
+ default: return QString("???");
+ }
+}
+
QString BitcoinUnits::name(int unit)
{
switch(unit)
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 451b52ee2..da34ed897 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -36,6 +36,8 @@ public:
static QList<Unit> availableUnits();
//! Is unit ID valid?
static bool valid(int unit);
+ //! Identifier, e.g. for image names
+ static QString id(int unit);
//! Short name
static QString name(int unit);
//! Longer description
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 9c9565be6..4c21eb559 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -85,7 +85,7 @@ QDateTime ClientModel::getLastBlockDate() const
if (chainActive.Tip())
return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
else
- return QDateTime::fromTime_t(Params().GenesisBlock().nTime); // Genesis block's time of current network
+ return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}
double ClientModel::getVerificationProgress() const
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index e0a524a55..b4ddda3ea 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -419,6 +419,8 @@ QString CoinControlDialog::getPriorityLabel(const CTxMemPool& pool, double dPrio
}
// Note: if mempool hasn't accumulated enough history (estimatePriority
// returns -1) we're conservative and classify as "lowest"
+ if (mempool.estimatePriority(nTxConfirmTarget) <= 0 && AllowFree(dPriority))
+ return ">=" + tr("medium");
return tr("lowest");
}
@@ -523,18 +525,21 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
sPriorityLabel = CoinControlDialog::getPriorityLabel(mempool, dPriority);
- // Fee
- int64_t nFee = payTxFee.GetFee(max((unsigned int)1000, nBytes));
+ // Voluntary Fee
+ nPayFee = payTxFee.GetFee(max((unsigned int)1000, nBytes));
// Min Fee
- nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+ if (nPayFee == 0)
+ {
+ nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
- double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
- if (dPriorityNeeded <= 0) // Not enough mempool history: never send free
- dPriorityNeeded = std::numeric_limits<double>::max();
+ double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
+ if (dPriorityNeeded <= 0 && !AllowFree(dPriority)) // not enough mempool history: never send free
+ dPriorityNeeded = std::numeric_limits<double>::max();
- if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded)
- nPayFee = 0;
+ if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded)
+ nPayFee = 0;
+ }
if (nPayAmount > 0)
{
@@ -615,7 +620,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546)));
// how many satoshis the estimated fee can vary per byte we guess wrong
- double dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
+ double dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), std::max(payTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK())) / 1000;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 0c5b8895a..1f535a4a6 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -329,26 +329,6 @@
</widget>
</item>
<item>
- <widget class="QLabel" name="socksVersionLabel">
- <property name="text">
- <string>SOCKS &amp;Version:</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="buddy">
- <cstring>socksVersion</cstring>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QValueComboBox" name="socksVersion">
- <property name="toolTip">
- <string>SOCKS version of the proxy (e.g. 5)</string>
- </property>
- </widget>
- </item>
- <item>
<spacer name="horizontalSpacer_1_Network">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index e66291278..8784da5f3 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>573</width>
+ <width>596</width>
<height>342</height>
</rect>
</property>
@@ -46,204 +46,369 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
- <widget class="QLabel" name="label_5">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Wallet</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="labelWalletStatus">
- <property name="toolTip">
- <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string>
- </property>
- <property name="styleSheet">
- <string notr="true">QLabel { color: red; }</string>
- </property>
- <property name="text">
- <string notr="true">(out of sync)</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Wallet</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelWalletStatus">
+ <property name="toolTip">
+ <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel { color: red; }</string>
+ </property>
+ <property name="text">
+ <string notr="true">(out of sync)</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
<item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <widget class="QLabel" name="labelWatchonly">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Watchonly:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
</item>
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <layout class="QFormLayout" name="formLayout_2">
- <property name="fieldGrowthPolicy">
- <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
- </property>
- <property name="horizontalSpacing">
- <number>12</number>
- </property>
- <property name="verticalSpacing">
- <number>12</number>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Available:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="labelBalance">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="toolTip">
- <string>Your current spendable balance</string>
- </property>
- <property name="text">
- <string notr="true">0 BTC</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Pending:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="labelUnconfirmed">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="toolTip">
- <string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
- </property>
- <property name="text">
- <string notr="true">0 BTC</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="labelImmatureText">
- <property name="text">
- <string>Immature:</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="labelImmature">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="toolTip">
- <string>Mined balance that has not yet matured</string>
- </property>
- <property name="text">
- <string notr="true">0 BTC</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="labelTotalText">
- <property name="text">
- <string>Total:</string>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="labelTotal">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="toolTip">
- <string>Your current total balance</string>
- </property>
- <property name="text">
- <string notr="true">0 BTC</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QFormLayout" name="formLayout_2">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <property name="horizontalSpacing">
+ <number>12</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>12</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Available:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="labelBalance">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Your current spendable balance</string>
+ </property>
+ <property name="text">
+ <string notr="true">0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Pending:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelUnconfirmed">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
+ </property>
+ <property name="text">
+ <string notr="true">0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelImmatureText">
+ <property name="text">
+ <string>Immature:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="labelImmature">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Mined balance that has not yet matured</string>
+ </property>
+ <property name="text">
+ <string notr="true">0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="labelTotalText">
+ <property name="text">
+ <string>Total:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="labelTotal">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Your current total balance</string>
+ </property>
+ <property name="text">
+ <string notr="true">0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="3" column="0" colspan="2">
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <property name="horizontalSpacing">
+ <number>12</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>12</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QLabel" name="labelWatchAvailable">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Your current balance in watchonly addresses</string>
+ </property>
+ <property name="text">
+ <string>0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="labelWatchPending">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Unconfirmed transactions to watchonly addresses</string>
+ </property>
+ <property name="text">
+ <string>0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="labelWatchImmature">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Mined balance in watchonly addresses that has not yet matured</string>
+ </property>
+ <property name="text">
+ <string>0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="Line" name="lineWatchBalance">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>140</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="labelWatchTotal">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>Current total balance in watchonly addresses</string>
+ </property>
+ <property name="text">
+ <string>0 BTC</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- </layout>
- </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
+ <width>20</width>
<height>20</height>
</size>
</property>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 9502dba90..597be40ab 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -52,15 +52,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
ui->proxyPort->setEnabled(false);
ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
- /** SOCKS version is only selectable for default proxy and is always 5 for IPv6 and Tor */
- ui->socksVersion->setEnabled(false);
- ui->socksVersion->addItem("5", 5);
- ui->socksVersion->addItem("4", 4);
- ui->socksVersion->setCurrentIndex(0);
-
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
- connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool)));
ui->proxyIp->installEventFilter(this);
@@ -182,7 +175,6 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
- mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion);
/* Window */
#ifndef Q_OS_MAC
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 09553a258..f07e66bf0 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -122,11 +122,6 @@ void OptionsModel::Init()
// Only try to set -proxy, if user has enabled fUseProxy
if (settings.value("fUseProxy").toBool() && !SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
addOverriddenOption("-proxy");
- if (!settings.contains("nSocksVersion"))
- settings.setValue("nSocksVersion", 5);
- // Only try to set -socks, if user has enabled fUseProxy
- if (settings.value("fUseProxy").toBool() && !SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()))
- addOverriddenOption("-socks");
// Display
if (!settings.contains("language"))
@@ -188,8 +183,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
return strlIpPort.at(1);
}
- case ProxySocksVersion:
- return settings.value("nSocksVersion", 5);
#ifdef ENABLE_WALLET
case Fee: {
@@ -284,13 +277,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
}
}
break;
- case ProxySocksVersion: {
- if (settings.value("nSocksVersion") != value) {
- settings.setValue("nSocksVersion", value.toInt());
- setRestartRequired(true);
- }
- }
- break;
#ifdef ENABLE_WALLET
case Fee: { // core option - can be changed on-the-fly
// Todo: Add is valid check and warn via message, if not
@@ -378,20 +364,16 @@ bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
// GUI settings can be overridden with -proxy.
proxyType curProxy;
if (GetProxy(NET_IPV4, curProxy)) {
- if (curProxy.second == 5) {
- proxy.setType(QNetworkProxy::Socks5Proxy);
- proxy.setHostName(QString::fromStdString(curProxy.first.ToStringIP()));
- proxy.setPort(curProxy.first.GetPort());
+ proxy.setType(QNetworkProxy::Socks5Proxy);
+ proxy.setHostName(QString::fromStdString(curProxy.ToStringIP()));
+ proxy.setPort(curProxy.GetPort());
- return true;
- }
- else
- return false;
+ return true;
}
else
proxy.setType(QNetworkProxy::NoProxy);
- return true;
+ return false;
}
void OptionsModel::setRestartRequired(bool fRequired)
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 1a9d1de57..1278f368c 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -103,6 +103,9 @@ OverviewPage::OverviewPage(QWidget *parent) :
currentBalance(-1),
currentUnconfirmedBalance(-1),
currentImmatureBalance(-1),
+ currentWatchOnlyBalance(-1),
+ currentWatchUnconfBalance(-1),
+ currentWatchImmatureBalance(-1),
txdelegate(new TxViewDelegate()),
filter(0)
{
@@ -135,22 +138,39 @@ OverviewPage::~OverviewPage()
delete ui;
}
-void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance)
+void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance)
{
int unit = walletModel->getOptionsModel()->getDisplayUnit();
currentBalance = balance;
currentUnconfirmedBalance = unconfirmedBalance;
currentImmatureBalance = immatureBalance;
+ currentWatchOnlyBalance = watchOnlyBalance;
+ currentWatchUnconfBalance = watchUnconfBalance;
+ currentWatchImmatureBalance = watchImmatureBalance;
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance));
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
bool showImmature = immatureBalance != 0;
- ui->labelImmature->setVisible(showImmature);
- ui->labelImmatureText->setVisible(showImmature);
+ bool showWatchOnlyImmature = watchImmatureBalance != 0;
+ bool showWatchOnly = (watchOnlyBalance != 0 || watchUnconfBalance != 0 || showWatchOnlyImmature);
+
+ // for symmetry reasons also show immature label when the watchonly one is shown
+ ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
+ ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature);
+ ui->labelWatchonly->setVisible(showWatchOnly); // show Watchonly label
+ ui->lineWatchBalance->setVisible(showWatchOnly); // show watchonly balance separator line
+ ui->labelWatchAvailable->setVisible(showWatchOnly); // show watchonly available balance
+ ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watchonly immature balance
+ ui->labelWatchPending->setVisible(showWatchOnly); // show watchonly pending balance
+ ui->labelWatchTotal->setVisible(showWatchOnly); // show watchonly total balance
}
void OverviewPage::setClientModel(ClientModel *model)
@@ -182,8 +202,9 @@ void OverviewPage::setWalletModel(WalletModel *model)
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
- setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance());
- connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64)));
+ setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
+ model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
+ connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
}
@@ -197,7 +218,8 @@ void OverviewPage::updateDisplayUnit()
if(walletModel && walletModel->getOptionsModel())
{
if(currentBalance != -1)
- setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance);
+ setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance,
+ currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance);
// Update txdelegate->unit with the current unit
txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit();
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 2507a3fb3..fe0010677 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -34,7 +34,8 @@ public:
void showOutOfSyncWarning(bool fShow);
public slots:
- void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance,
+ qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance);
signals:
void transactionClicked(const QModelIndex &index);
@@ -46,6 +47,9 @@ private:
qint64 currentBalance;
qint64 currentUnconfirmedBalance;
qint64 currentImmatureBalance;
+ qint64 currentWatchOnlyBalance;
+ qint64 currentWatchUnconfBalance;
+ qint64 currentWatchImmatureBalance;
TxViewDelegate *txdelegate;
TransactionFilterProxy *filter;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 53db2c5cd..7c0c95c45 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -214,9 +214,13 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
if (readPaymentRequest(arg, request))
{
if (request.getDetails().network() == "main")
+ {
SelectParams(CBaseChainParams::MAIN);
- else
+ }
+ else if (request.getDetails().network() == "test")
+ {
SelectParams(CBaseChainParams::TESTNET);
+ }
}
}
else
@@ -341,20 +345,14 @@ void PaymentServer::initNetManager()
QNetworkProxy proxy;
- // Query active proxy (fails if no SOCKS5 proxy)
+ // Query active SOCKS5 proxy
if (optionsModel->getProxySettings(proxy)) {
- if (proxy.type() == QNetworkProxy::Socks5Proxy) {
- netManager->setProxy(proxy);
+ netManager->setProxy(proxy);
- qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
- }
- else
- qDebug() << "PaymentServer::initNetManager : No active proxy server found.";
+ qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
}
else
- emit message(tr("Net manager warning"),
- tr("Your active proxy doesn't support SOCKS5, which is required for payment requests via proxy."),
- CClientUIInterface::MSG_WARNING);
+ qDebug() << "PaymentServer::initNetManager : No active proxy server found.";
connect(netManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(netRequestFinished(QNetworkReply*)));
diff --git a/src/qt/res/icons/unit_btc.png b/src/qt/res/icons/unit_btc.png
new file mode 100644
index 000000000..ec3497435
--- /dev/null
+++ b/src/qt/res/icons/unit_btc.png
Binary files differ
diff --git a/src/qt/res/icons/unit_mbtc.png b/src/qt/res/icons/unit_mbtc.png
new file mode 100644
index 000000000..32bf2f2ca
--- /dev/null
+++ b/src/qt/res/icons/unit_mbtc.png
Binary files differ
diff --git a/src/qt/res/icons/unit_ubtc.png b/src/qt/res/icons/unit_ubtc.png
new file mode 100644
index 000000000..d5a154882
--- /dev/null
+++ b/src/qt/res/icons/unit_ubtc.png
Binary files differ
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index b7d74d703..6f10ed5b0 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -90,8 +90,9 @@ void SendCoinsDialog::setModel(WalletModel *model)
}
}
- setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance());
- connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64)));
+ setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
+ model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
+ connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
// Coin Control
@@ -383,10 +384,14 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
return true;
}
-void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance)
+void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance,
+ qint64 watchBalance, qint64 watchUnconfirmedBalance, qint64 watchImmatureBalance)
{
Q_UNUSED(unconfirmedBalance);
Q_UNUSED(immatureBalance);
+ Q_UNUSED(watchBalance);
+ Q_UNUSED(watchUnconfirmedBalance);
+ Q_UNUSED(watchImmatureBalance);
if(model && model->getOptionsModel())
{
@@ -396,7 +401,7 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint
void SendCoinsDialog::updateDisplayUnit()
{
- setBalance(model->getBalance(), 0, 0);
+ setBalance(model->getBalance(), 0, 0, 0, 0, 0);
}
void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index fcae26c72..6cdf4a00c 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -47,7 +47,8 @@ public slots:
void accept();
SendCoinsEntry *addEntry();
void updateTabsAndLabels();
- void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance,
+ qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance);
private:
Ui::SendCoinsDialog *ui;
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index e48dbcac9..36e4d50e3 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -15,6 +15,7 @@
#include "timedata.h"
#include "ui_interface.h"
#include "wallet.h"
+#include "script.h"
#include <stdint.h>
#include <string>
@@ -54,8 +55,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
int64_t nTime = wtx.GetTxTime();
- int64_t nCredit = wtx.GetCredit();
- int64_t nDebit = wtx.GetDebit();
+ int64_t nCredit = wtx.GetCredit(ISMINE_ALL);
+ int64_t nDebit = wtx.GetDebit(ISMINE_ALL);
int64_t nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx);
@@ -97,10 +98,11 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
strHTML += "<b>" + tr("To") + ":</b> ";
strHTML += GUIUtil::HtmlEscape(rec->address);
+ QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only");
if (!wallet->mapAddressBook[address].name.empty())
- strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
+ strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
else
- strHTML += " (" + tr("own address") + ")";
+ strHTML += " (" + addressOwned + ")";
strHTML += "<br>";
}
}
@@ -131,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
int64_t nUnmatured = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
- nUnmatured += wallet->GetCredit(txout);
+ nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
@@ -148,22 +150,33 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
}
else
{
- bool fAllFromMe = true;
+ isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
- fAllFromMe = fAllFromMe && wallet->IsMine(txin);
+ {
+ isminetype mine = wallet->IsMine(txin);
+ if(fAllFromMe > mine) fAllFromMe = mine;
+ }
- bool fAllToMe = true;
+ isminetype fAllToMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
- fAllToMe = fAllToMe && wallet->IsMine(txout);
+ {
+ isminetype mine = wallet->IsMine(txout);
+ if(fAllToMe > mine) fAllToMe = mine;
+ }
if (fAllFromMe)
{
+ if(fAllFromMe == ISMINE_WATCH_ONLY)
+ strHTML += "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
+
//
// Debit
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
- if (wallet->IsMine(txout))
+ // Ignore change
+ isminetype toSelf = wallet->IsMine(txout);
+ if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
continue;
if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
@@ -176,11 +189,17 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString());
+ if(toSelf == ISMINE_SPENDABLE)
+ strHTML += " (own address)";
+ else if(toSelf == ISMINE_WATCH_ONLY)
+ strHTML += " (watch-only)";
strHTML += "<br>";
}
}
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>";
+ if(toSelf)
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>";
}
if (fAllToMe)
@@ -188,8 +207,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Payment to self
int64_t nChange = wtx.GetChange();
int64_t nValue = nCredit - nChange;
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
+ strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
+ strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
}
int64_t nTxFee = nDebit - wtx.GetValueOut();
@@ -203,10 +222,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if (wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
}
}
@@ -256,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if(wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
@@ -286,7 +305,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
}
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue);
- strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "</li>";
+ strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
+ strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
}
}
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 21f1b7356..7d29c212b 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -33,7 +33,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime();
int64_t nCredit = wtx.GetCredit(true);
- int64_t nDebit = wtx.GetDebit();
+ int64_t nDebit = wtx.GetDebit(ISMINE_ALL);
int64_t nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash();
std::map<std::string, std::string> mapValue = wtx.mapValue;
@@ -45,12 +45,14 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
- if(wallet->IsMine(txout))
+ isminetype mine = wallet->IsMine(txout);
+ if(mine)
{
TransactionRecord sub(hash, nTime);
CTxDestination address;
sub.idx = parts.size(); // sequence number
sub.credit = txout.nValue;
+ sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
{
// Received by Bitcoin Address
@@ -75,13 +77,22 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
}
else
{
- bool fAllFromMe = true;
+ bool involvesWatchAddress = false;
+ isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
- fAllFromMe = fAllFromMe && wallet->IsMine(txin);
+ {
+ isminetype mine = wallet->IsMine(txin);
+ if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
+ if(fAllFromMe > mine) fAllFromMe = mine;
+ }
- bool fAllToMe = true;
+ isminetype fAllToMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
- fAllToMe = fAllToMe && wallet->IsMine(txout);
+ {
+ isminetype mine = wallet->IsMine(txout);
+ if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
+ if(fAllToMe > mine) fAllToMe = mine;
+ }
if (fAllFromMe && fAllToMe)
{
@@ -90,6 +101,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
+ parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe)
{
@@ -103,6 +115,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
+ sub.involvesWatchAddress = involvesWatchAddress;
if(wallet->IsMine(txout))
{
@@ -143,6 +156,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
// Mixed debit transaction, can't break down payees
//
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
+ parts.last().involvesWatchAddress = involvesWatchAddress;
}
}
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 37679cebf..d3cfa77d9 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -137,6 +137,9 @@ public:
/** Status: can change with block chain update */
TransactionStatus status;
+ /** Whether the transaction was sent/received with a watch-only address */
+ bool involvesWatchAddress;
+
/** Return the unique identifier for this transaction (part) */
QString getTxID() const;
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 6c1a6c357..3605cc1ba 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -396,19 +396,22 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
{
+ // mark transactions involving watch-only addresses:
+ QString watchAddress = wtx->involvesWatchAddress ? " (w) " : "";
+
switch(wtx->type)
{
case TransactionRecord::RecvFromOther:
- return QString::fromStdString(wtx->address);
+ return QString::fromStdString(wtx->address) + watchAddress;
case TransactionRecord::RecvWithAddress:
case TransactionRecord::SendToAddress:
case TransactionRecord::Generated:
- return lookupAddress(wtx->address, tooltip);
+ return lookupAddress(wtx->address, tooltip) + watchAddress;
case TransactionRecord::SendToOther:
- return QString::fromStdString(wtx->address);
+ return QString::fromStdString(wtx->address) + watchAddress;
case TransactionRecord::SendToSelf:
default:
- return tr("(n/a)");
+ return tr("(n/a)") + watchAddress;
}
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index defc815de..7317c3276 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -60,7 +60,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const
std::vector<COutput> vCoins;
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
- nBalance += out.tx->vout[out.i].nValue;
+ if(out.fSpendable)
+ nBalance += out.tx->vout[out.i].nValue;
return nBalance;
}
@@ -78,6 +79,21 @@ qint64 WalletModel::getImmatureBalance() const
return wallet->GetImmatureBalance();
}
+qint64 WalletModel::getWatchBalance() const
+{
+ return wallet->GetWatchOnlyBalance();
+}
+
+qint64 WalletModel::getWatchUnconfirmedBalance() const
+{
+ return wallet->GetUnconfirmedWatchOnlyBalance();
+}
+
+qint64 WalletModel::getWatchImmatureBalance() const
+{
+ return wallet->GetImmatureWatchOnlyBalance();
+}
+
int WalletModel::getNumTransactions() const
{
int numTransactions = 0;
@@ -126,13 +142,21 @@ void WalletModel::checkBalanceChanged()
qint64 newBalance = getBalance();
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
qint64 newImmatureBalance = getImmatureBalance();
+ qint64 newWatchOnlyBalance = getWatchBalance();
+ qint64 newWatchUnconfBalance = getWatchUnconfirmedBalance();
+ qint64 newWatchImmatureBalance = getWatchImmatureBalance();
- if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
+ if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance ||
+ cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance)
{
cachedBalance = newBalance;
cachedUnconfirmedBalance = newUnconfirmedBalance;
cachedImmatureBalance = newImmatureBalance;
- emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance);
+ cachedWatchOnlyBalance = newWatchOnlyBalance;
+ cachedWatchUnconfBalance = newWatchUnconfBalance;
+ cachedWatchImmatureBalance = newWatchImmatureBalance;
+ emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance,
+ newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance);
}
}
@@ -543,7 +567,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
if (!wallet->mapWallet.count(outpoint.hash)) continue;
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
- COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
vOutputs.push_back(out);
}
}
@@ -570,7 +594,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
if (!wallet->mapWallet.count(outpoint.hash)) continue;
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
- COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
+ COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
vCoins.push_back(out);
}
@@ -581,11 +605,12 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
{
if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
- cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0);
+ cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true);
}
CTxDestination address;
- if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue;
+ if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
+ continue;
mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out);
}
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index ccf590aae..7ad54ff8e 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -128,6 +128,9 @@ public:
qint64 getBalance(const CCoinControl *coinControl = NULL) const;
qint64 getUnconfirmedBalance() const;
qint64 getImmatureBalance() const;
+ qint64 getWatchBalance() const;
+ qint64 getWatchUnconfirmedBalance() const;
+ qint64 getWatchImmatureBalance() const;
int getNumTransactions() const;
EncryptionStatus getEncryptionStatus() const;
@@ -206,6 +209,9 @@ private:
qint64 cachedBalance;
qint64 cachedUnconfirmedBalance;
qint64 cachedImmatureBalance;
+ qint64 cachedWatchOnlyBalance;
+ qint64 cachedWatchUnconfBalance;
+ qint64 cachedWatchImmatureBalance;
qint64 cachedNumTransactions;
EncryptionStatus cachedEncryptionStatus;
int cachedNumBlocks;
@@ -218,7 +224,8 @@ private:
signals:
// Signal that balance in wallet changed
- void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance);
+ void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance,
+ qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance);
// Number of transactions in wallet changed
void numTransactionsChanged(int count);
diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index f8f9bf45b..a06f42f66 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -6,11 +6,14 @@
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
#include "init.h"
+#include "util.h"
#include <windows.h>
#include <QDebug>
+#include <openssl/rand.h>
+
// If we don't want a message to be processed by Qt, return true and set result to
// the value that the window procedure should return. Otherwise return false.
bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult)
@@ -19,6 +22,16 @@ bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pM
MSG *pMsg = static_cast<MSG *>(pMessage);
+ // Seed OpenSSL PRNG with Windows event data (e.g. mouse movements and other user interactions)
+ if (RAND_event(pMsg->message, pMsg->wParam, pMsg->lParam) == 0) {
+ // Warn only once as this is performance-critical
+ static bool warned = false;
+ if (!warned) {
+ LogPrint("%s: OpenSSL RAND_event() failed to seed OpenSSL PRNG with enough data.\n", __func__);
+ warned = true;
+ }
+ }
+
switch(pMsg->message)
{
case WM_QUERYENDSESSION:
@@ -45,13 +58,13 @@ void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, c
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
if (shutdownBRCreate == NULL) {
- qWarning() << "registerShutdownBlockReason : GetProcAddress for ShutdownBlockReasonCreate failed";
+ qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed";
return;
}
if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
- qWarning() << "registerShutdownBlockReason : Successfully registered: " + strReason;
+ qWarning() << "registerShutdownBlockReason: Successfully registered: " + strReason;
else
- qWarning() << "registerShutdownBlockReason : Failed to register: " + strReason;
+ qWarning() << "registerShutdownBlockReason: Failed to register: " + strReason;
}
#endif
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 501940a73..5edeecf93 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -37,9 +37,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getreceivedbyaccount", 1 },
{ "listreceivedbyaddress", 0 },
{ "listreceivedbyaddress", 1 },
+ { "listreceivedbyaddress", 2 },
{ "listreceivedbyaccount", 0 },
{ "listreceivedbyaccount", 1 },
+ { "listreceivedbyaccount", 2 },
{ "getbalance", 1 },
+ { "getbalance", 2 },
{ "getblockhash", 0 },
{ "move", 2 },
{ "move", 3 },
@@ -47,10 +50,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendfrom", 3 },
{ "listtransactions", 1 },
{ "listtransactions", 2 },
+ { "listtransactions", 3 },
{ "listaccounts", 0 },
+ { "listaccounts", 1 },
{ "walletpassphrase", 1 },
{ "getblocktemplate", 0 },
{ "listsinceblock", 1 },
+ { "listsinceblock", 2 },
{ "sendmany", 1 },
{ "sendmany", 2 },
{ "addmultisigaddress", 0 },
@@ -72,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "lockunspent", 0 },
{ "lockunspent", 1 },
{ "importprivkey", 2 },
+ { "importaddress", 2 },
{ "verifychain", 0 },
{ "verifychain", 1 },
{ "keypoolrefill", 0 },
@@ -126,7 +133,6 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
throw runtime_error(string("Error parsing JSON:")+strVal);
params.push_back(jVal);
}
-
}
return params;
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 593e0d2b6..4193f41b4 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -133,6 +133,60 @@ Value importprivkey(const Array& params, bool fHelp)
return Value::null;
}
+Value importaddress(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 3)
+ throw runtime_error(
+ "importaddress <address> [label] [rescan=true]\n"
+ "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.");
+
+ CScript script;
+
+ CBitcoinAddress address(params[0].get_str());
+ if (address.IsValid()) {
+ script.SetDestination(address.Get());
+ } else if (IsHex(params[0].get_str())) {
+ std::vector<unsigned char> data(ParseHex(params[0].get_str()));
+ script = CScript(data.begin(), data.end());
+ } else {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
+ }
+
+ string strLabel = "";
+ if (params.size() > 1)
+ strLabel = params[1].get_str();
+
+ // Whether to perform rescan after import
+ bool fRescan = true;
+ if (params.size() > 2)
+ fRescan = params[2].get_bool();
+
+ {
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+
+ // add to address book or update label
+ if (address.IsValid())
+ pwalletMain->SetAddressBook(address.Get(), strLabel, "receive");
+
+ // Don't throw error in case an address is already there
+ if (pwalletMain->HaveWatchOnly(script))
+ return Value::null;
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->AddWatchOnly(script))
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+
+ if (fRescan)
+ {
+ pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
+ pwalletMain->ReacceptWalletTransactions();
+ }
+ }
+
+ return Value::null;
+}
+
Value importwallet(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
@@ -157,7 +211,7 @@ Value importwallet(const Array& params, bool fHelp)
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
- int64_t nTimeBegin = chainActive.Tip()->nTime;
+ int64_t nTimeBegin = chainActive.Tip()->GetBlockTime();
bool fGood = true;
@@ -215,7 +269,7 @@ Value importwallet(const Array& params, bool fHelp)
pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI
CBlockIndex *pindex = chainActive.Tip();
- while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
+ while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200)
pindex = pindex->pprev;
if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
@@ -301,7 +355,7 @@ Value dumpwallet(const Array& params, bool fHelp)
file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
- file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime));
+ file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
file << "\n";
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index db60ef359..c7621dc13 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -442,7 +442,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
result.push_back(Pair("noncerange", "00000000ffffffff"));
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
- result.push_back(Pair("curtime", (int64_t)pblock->nTime));
+ result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index 9ed6b5251..cff795bdf 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -72,7 +72,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
- obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
+ obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET));
#ifdef ENABLE_WALLET
@@ -92,36 +92,45 @@ Value getinfo(const Array& params, bool fHelp)
#ifdef ENABLE_WALLET
class DescribeAddressVisitor : public boost::static_visitor<Object>
{
+private:
+ isminetype mine;
+
public:
+ DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
+
Object operator()(const CNoDestination &dest) const { return Object(); }
Object operator()(const CKeyID &keyID) const {
Object obj;
CPubKey vchPubKey;
- pwalletMain->GetPubKey(keyID, vchPubKey);
obj.push_back(Pair("isscript", false));
- obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
- obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
+ if (mine == ISMINE_SPENDABLE) {
+ pwalletMain->GetPubKey(keyID, vchPubKey);
+ obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
+ obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
+ }
return obj;
}
Object operator()(const CScriptID &scriptID) const {
Object obj;
obj.push_back(Pair("isscript", true));
- CScript subscript;
- pwalletMain->GetCScript(scriptID, subscript);
- std::vector<CTxDestination> addresses;
- txnouttype whichType;
- int nRequired;
- ExtractDestinations(subscript, whichType, addresses, nRequired);
- obj.push_back(Pair("script", GetTxnOutputType(whichType)));
- obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
- Array a;
- BOOST_FOREACH(const CTxDestination& addr, addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
- obj.push_back(Pair("addresses", a));
- if (whichType == TX_MULTISIG)
- obj.push_back(Pair("sigsrequired", nRequired));
+ if (mine != ISMINE_NO) {
+ CScript subscript;
+ pwalletMain->GetCScript(scriptID, subscript);
+ std::vector<CTxDestination> addresses;
+ txnouttype whichType;
+ int nRequired;
+ ExtractDestinations(subscript, whichType, addresses, nRequired);
+ obj.push_back(Pair("script", GetTxnOutputType(whichType)));
+ obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
+ Array a;
+ BOOST_FOREACH(const CTxDestination& addr, addresses)
+ a.push_back(CBitcoinAddress(addr).ToString());
+ obj.push_back(Pair("addresses", a));
+ if (whichType == TX_MULTISIG)
+ obj.push_back(Pair("sigsrequired", nRequired));
+ }
return obj;
}
};
@@ -161,10 +170,11 @@ Value validateaddress(const Array& params, bool fHelp)
string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress));
#ifdef ENABLE_WALLET
- bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false;
- ret.push_back(Pair("ismine", fMine));
- if (fMine) {
- Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
+ isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
+ ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
+ if (mine != ISMINE_NO) {
+ ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
+ Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
ret.insert(ret.end(), detail.begin(), detail.end());
}
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index cd3bd59f8..cf2c293ca 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -371,7 +371,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices)));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
- obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
+ obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
Array localAddresses;
{
diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp
index dd8692e80..9e18ca847 100644
--- a/src/rpcprotocol.cpp
+++ b/src/rpcprotocol.cpp
@@ -25,6 +25,9 @@ using namespace boost;
using namespace boost::asio;
using namespace json_spirit;
+// Number of bytes to allocate and read at most at once in post data
+const size_t POST_READ_SIZE = 256 * 1024;
+
//
// HTTP protocol
//
@@ -204,8 +207,17 @@ int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
// Read message
if (nLen > 0)
{
- vector<char> vch(nLen);
- stream.read(&vch[0], nLen);
+ vector<char> vch;
+ size_t ptr = 0;
+ while (ptr < (size_t)nLen)
+ {
+ size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
+ vch.resize(ptr + bytes_to_read);
+ stream.read(&vch[ptr], bytes_to_read);
+ if (!stream) // Connection lost while reading
+ return HTTP_INTERNAL_SERVER_ERROR;
+ ptr += bytes_to_read;
+ }
strMessageRet = string(vch.begin(), vch.end());
}
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 9771f8e68..2306b1b88 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -99,8 +99,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
if (chainActive.Contains(pindex))
{
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
- entry.push_back(Pair("time", (int64_t)pindex->nTime));
- entry.push_back(Pair("blocktime", (int64_t)pindex->nTime));
+ entry.push_back(Pair("time", pindex->GetBlockTime()));
+ entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
}
else
entry.push_back(Pair("confirmations", 0));
@@ -304,6 +304,7 @@ Value listunspent(const Array& params, bool fHelp)
}
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth));
+ entry.push_back(Pair("spendable", out.fSpendable));
results.push_back(entry);
}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 5c4d30749..18fa07510 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -282,6 +282,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getwalletinfo", &getwalletinfo, true, false, true },
{ "importprivkey", &importprivkey, false, false, true },
{ "importwallet", &importwallet, false, false, true },
+ { "importaddress", &importaddress, false, false, true },
{ "keypoolrefill", &keypoolrefill, true, false, true },
{ "listaccounts", &listaccounts, false, false, true },
{ "listaddressgroupings", &listaddressgroupings, false, false, true },
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 01e77163c..e32eb975a 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -131,6 +131,7 @@ extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fH
extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 38e96133b..1e4612906 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -50,7 +50,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
{
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
entry.push_back(Pair("blockindex", wtx.nIndex));
- entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
+ entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
}
uint256 hash = wtx.GetHash();
entry.push_back(Pair("txid", hash.GetHex()));
@@ -346,7 +346,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
EnsureWalletIsUnlocked();
- string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
+ string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
@@ -557,7 +557,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
}
-int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
+int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
{
int64_t nBalance = 0;
@@ -569,7 +569,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
continue;
int64_t nReceived, nSent, nFee;
- wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee);
+ wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
nBalance += nReceived;
@@ -582,18 +582,18 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
return nBalance;
}
-int64_t GetAccountBalance(const string& strAccount, int nMinDepth)
+int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
{
CWalletDB walletdb(pwalletMain->strWalletFile);
- return GetAccountBalance(walletdb, strAccount, nMinDepth);
+ return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
}
Value getbalance(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 2)
+ if (fHelp || params.size() > 3)
throw runtime_error(
- "getbalance ( \"account\" minconf )\n"
+ "getbalance ( \"account\" minconf includeWatchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"If account is specified, returns the balance in the account.\n"
"Note that the account \"\" is not the same as leaving the parameter out.\n"
@@ -601,6 +601,7 @@ Value getbalance(const Array& params, bool fHelp)
"\nArguments:\n"
"1. \"account\" (string, optional) The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
+ "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in btc received for this account.\n"
"\nExamples:\n"
@@ -622,6 +623,10 @@ Value getbalance(const Array& params, bool fHelp)
int nMinDepth = 1;
if (params.size() > 1)
nMinDepth = params[1].get_int();
+ isminefilter filter = ISMINE_SPENDABLE;
+ if(params.size() > 2)
+ if(params[2].get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
if (params[0].get_str() == "*") {
// Calculate total balance a different way from GetBalance()
@@ -638,7 +643,7 @@ Value getbalance(const Array& params, bool fHelp)
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
- wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount);
+ wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
@@ -653,7 +658,7 @@ Value getbalance(const Array& params, bool fHelp)
string strAccount = AccountFromValue(params[0]);
- int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
+ int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
return ValueFromAmount(nBalance);
}
@@ -781,12 +786,12 @@ Value sendfrom(const Array& params, bool fHelp)
EnsureWalletIsUnlocked();
// Check funds
- int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
+ int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
// Send
- string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
+ string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
@@ -858,7 +863,7 @@ Value sendmany(const Array& params, bool fHelp)
EnsureWalletIsUnlocked();
// Check funds
- int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
+ int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -927,10 +932,12 @@ struct tallyitem
int64_t nAmount;
int nConf;
vector<uint256> txids;
+ bool fIsWatchonly;
tallyitem()
{
nAmount = 0;
nConf = std::numeric_limits<int>::max();
+ fIsWatchonly = false;
}
};
@@ -946,6 +953,11 @@ Value ListReceived(const Array& params, bool fByAccounts)
if (params.size() > 1)
fIncludeEmpty = params[1].get_bool();
+ isminefilter filter = ISMINE_SPENDABLE;
+ if(params.size() > 2)
+ if(params[2].get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
+
// Tally
map<CBitcoinAddress, tallyitem> mapTally;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
@@ -962,13 +974,19 @@ Value ListReceived(const Array& params, bool fByAccounts)
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
CTxDestination address;
- if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
+ if (!ExtractDestination(txout.scriptPubKey, address))
+ continue;
+
+ isminefilter mine = IsMine(*pwalletMain, address);
+ if(!(mine & filter))
continue;
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = min(item.nConf, nDepth);
item.txids.push_back(wtx.GetHash());
+ if (mine & ISMINE_WATCH_ONLY)
+ item.fIsWatchonly = true;
}
}
@@ -985,10 +1003,12 @@ Value ListReceived(const Array& params, bool fByAccounts)
int64_t nAmount = 0;
int nConf = std::numeric_limits<int>::max();
+ bool fIsWatchonly = false;
if (it != mapTally.end())
{
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
+ fIsWatchonly = (*it).second.fIsWatchonly;
}
if (fByAccounts)
@@ -996,10 +1016,13 @@ Value ListReceived(const Array& params, bool fByAccounts)
tallyitem& item = mapAccountTally[strAccount];
item.nAmount += nAmount;
item.nConf = min(item.nConf, nConf);
+ item.fIsWatchonly = fIsWatchonly;
}
else
{
Object obj;
+ if(fIsWatchonly)
+ obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
@@ -1024,6 +1047,8 @@ Value ListReceived(const Array& params, bool fByAccounts)
int64_t nAmount = (*it).second.nAmount;
int nConf = (*it).second.nConf;
Object obj;
+ if((*it).second.fIsWatchonly)
+ obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("account", (*it).first));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
@@ -1036,17 +1061,19 @@ Value ListReceived(const Array& params, bool fByAccounts)
Value listreceivedbyaddress(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 2)
+ if (fHelp || params.size() > 3)
throw runtime_error(
- "listreceivedbyaddress ( minconf includeempty )\n"
+ "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (numeric, optional, dafault=false) Whether to include addresses that haven't received any payments.\n"
+ "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
+ " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving address\n"
" \"account\" : \"accountname\", (string) The account of the receiving address. The default account is \"\".\n"
" \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n"
@@ -1058,7 +1085,7 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaddress", "")
+ HelpExampleCli("listreceivedbyaddress", "6 true")
- + HelpExampleRpc("listreceivedbyaddress", "6, true")
+ + HelpExampleRpc("listreceivedbyaddress", "6, true, true")
);
return ListReceived(params, false);
@@ -1066,17 +1093,19 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
Value listreceivedbyaccount(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 2)
+ if (fHelp || params.size() > 3)
throw runtime_error(
- "listreceivedbyaccount ( minconf includeempty )\n"
+ "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n"
"\nList balances by account.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n"
+ "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
+ " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) The account name of the receiving account\n"
" \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n"
" \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
@@ -1087,7 +1116,7 @@ Value listreceivedbyaccount(const Array& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaccount", "")
+ HelpExampleCli("listreceivedbyaccount", "6 true")
- + HelpExampleRpc("listreceivedbyaccount", "6, true")
+ + HelpExampleRpc("listreceivedbyaccount", "6, true, true")
);
return ListReceived(params, true);
@@ -1100,16 +1129,17 @@ static void MaybePushAddress(Object & entry, const CTxDestination &dest)
entry.push_back(Pair("address", addr.ToString()));
}
-void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
+void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
{
int64_t nFee;
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
- wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
+ wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
bool fAllAccounts = (strAccount == string("*"));
+ bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
@@ -1117,6 +1147,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
{
Object entry;
+ if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & ISMINE_WATCH_ONLY))
+ entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount));
MaybePushAddress(entry, s.first);
entry.push_back(Pair("category", "send"));
@@ -1139,6 +1171,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
if (fAllAccounts || (account == strAccount))
{
Object entry;
+ if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & ISMINE_WATCH_ONLY))
+ entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account));
MaybePushAddress(entry, r.first);
if (wtx.IsCoinBase())
@@ -1182,16 +1216,16 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar
Value listtransactions(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 3)
+ if (fHelp || params.size() > 4)
throw runtime_error(
- "listtransactions ( \"account\" count from )\n"
+ "listtransactions ( \"account\" count from includeWatchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) The account name. If not included, it will list all transactions for all accounts.\n"
" If \"\" is set, it will list transactions for the default account.\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
"3. from (numeric, optional, default=0) The number of transactions to skip\n"
-
+ "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
@@ -1251,6 +1285,10 @@ Value listtransactions(const Array& params, bool fHelp)
int nFrom = 0;
if (params.size() > 2)
nFrom = params[2].get_int();
+ isminefilter filter = ISMINE_SPENDABLE;
+ if(params.size() > 3)
+ if(params[3].get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
@@ -1267,7 +1305,7 @@ Value listtransactions(const Array& params, bool fHelp)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0)
- ListTransactions(*pwtx, strAccount, 0, true, ret);
+ ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
CAccountingEntry *const pacentry = (*it).second.second;
if (pacentry != 0)
AcentryToJSON(*pacentry, strAccount, ret);
@@ -1295,12 +1333,13 @@ Value listtransactions(const Array& params, bool fHelp)
Value listaccounts(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 1)
+ if (fHelp || params.size() > 2)
throw runtime_error(
- "listaccounts ( minconf )\n"
+ "listaccounts ( minconf includeWatchonly)\n"
"\nReturns Object that has account names as keys, account balances as values.\n"
"\nArguments:\n"
- "1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n"
+ "1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n"
+ "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"{ (json object where keys are account names, and values are numeric balances\n"
" \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n"
@@ -1320,10 +1359,14 @@ Value listaccounts(const Array& params, bool fHelp)
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
+ isminefilter includeWatchonly = ISMINE_SPENDABLE;
+ if(params.size() > 1)
+ if(params[1].get_bool())
+ includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
map<string, int64_t> mapAccountBalances;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) {
- if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
+ if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me
mapAccountBalances[entry.second.name] = 0;
}
@@ -1337,7 +1380,7 @@ Value listaccounts(const Array& params, bool fHelp)
int nDepth = wtx.GetDepthInMainChain();
if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0)
continue;
- wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
+ wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
mapAccountBalances[strSentAccount] -= s.second;
@@ -1367,11 +1410,12 @@ Value listsinceblock(const Array& params, bool fHelp)
{
if (fHelp)
throw runtime_error(
- "listsinceblock ( \"blockhash\" target-confirmations )\n"
+ "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
"2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
+ "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
@@ -1407,6 +1451,7 @@ Value listsinceblock(const Array& params, bool fHelp)
CBlockIndex *pindex = NULL;
int target_confirms = 1;
+ isminefilter filter = ISMINE_SPENDABLE;
if (params.size() > 0)
{
@@ -1426,6 +1471,10 @@ Value listsinceblock(const Array& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
+ if(params.size() > 2)
+ if(params[2].get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
+
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
Array transactions;
@@ -1435,7 +1484,7 @@ Value listsinceblock(const Array& params, bool fHelp)
CWalletTx tx = (*it).second;
if (depth == -1 || tx.GetDepthInMainChain() < depth)
- ListTransactions(tx, "*", 0, true, transactions);
+ ListTransactions(tx, "*", 0, true, transactions, filter);
}
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
@@ -1450,12 +1499,13 @@ Value listsinceblock(const Array& params, bool fHelp)
Value gettransaction(const Array& params, bool fHelp)
{
- if (fHelp || params.size() != 1)
+ if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"gettransaction \"txid\"\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
+ "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount in btc\n"
@@ -1492,24 +1542,29 @@ Value gettransaction(const Array& params, bool fHelp)
uint256 hash;
hash.SetHex(params[0].get_str());
+ isminefilter filter = ISMINE_SPENDABLE;
+ if(params.size() > 1)
+ if(params[1].get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
+
Object entry;
if (!pwalletMain->mapWallet.count(hash))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
const CWalletTx& wtx = pwalletMain->mapWallet[hash];
- int64_t nCredit = wtx.GetCredit();
- int64_t nDebit = wtx.GetDebit();
+ int64_t nCredit = wtx.GetCredit(filter);
+ int64_t nDebit = wtx.GetDebit(filter);
int64_t nNet = nCredit - nDebit;
- int64_t nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
+ int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
- if (wtx.IsFromMe())
+ if (wtx.IsFromMe(filter))
entry.push_back(Pair("fee", ValueFromAmount(nFee)));
WalletTxToJSON(wtx, entry);
Array details;
- ListTransactions(wtx, "*", 0, false, details);
+ ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/script.cpp b/src/script.cpp
index e1b698540..238a25e72 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -1456,36 +1456,49 @@ public:
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
};
-bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
+isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
{
- return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest);
+ CScript script;
+ script.SetDestination(dest);
+ return IsMine(keystore, script);
}
-bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
+isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txnouttype whichType;
- if (!Solver(scriptPubKey, whichType, vSolutions))
- return false;
+ if (!Solver(scriptPubKey, whichType, vSolutions)) {
+ if (keystore.HaveWatchOnly(scriptPubKey))
+ return ISMINE_WATCH_ONLY;
+ return ISMINE_NO;
+ }
CKeyID keyID;
switch (whichType)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
- return false;
+ break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
- return keystore.HaveKey(keyID);
+ if (keystore.HaveKey(keyID))
+ return ISMINE_SPENDABLE;
+ break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
- return keystore.HaveKey(keyID);
+ if (keystore.HaveKey(keyID))
+ return ISMINE_SPENDABLE;
+ break;
case TX_SCRIPTHASH:
{
+ CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
- if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
- return false;
- return IsMine(keystore, subscript);
+ if (keystore.GetCScript(scriptID, subscript)) {
+ isminetype ret = IsMine(keystore, subscript);
+ if (ret == ISMINE_SPENDABLE)
+ return ret;
+ }
+ break;
}
case TX_MULTISIG:
{
@@ -1495,10 +1508,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
- return HaveKeys(keys, keystore) == keys.size();
+ if (HaveKeys(keys, keystore) == keys.size())
+ return ISMINE_SPENDABLE;
+ break;
}
}
- return false;
+
+ if (keystore.HaveWatchOnly(scriptPubKey))
+ return ISMINE_WATCH_ONLY;
+ return ISMINE_NO;
}
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
diff --git a/src/script.h b/src/script.h
index 7ab471f6e..e36be2db9 100644
--- a/src/script.h
+++ b/src/script.h
@@ -194,6 +194,17 @@ enum
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
};
+/** IsMine() return codes */
+enum isminetype
+{
+ ISMINE_NO = 0,
+ ISMINE_WATCH_ONLY = 1,
+ ISMINE_SPENDABLE = 2,
+ ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
+};
+/** used for bitflags of isminetype */
+typedef uint8_t isminefilter;
+
// Mandatory script verification flags that all new blocks must comply with for
// them to be valid. (but old blocks may not comply with) Currently just P2SH,
// but in the future other flags may be added, such as a soft-fork to enforce
@@ -801,8 +812,8 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
-bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
-bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 5c6c7d858..2cdafa4bd 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
{
// Same test as bloom_create_insert_serialize, but we add a nTweak of 100
- CBloomFilter filter(3, 0.01, 2147483649, BLOOM_UPDATE_ALL);
+ CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index ea301685c..11762c6ea 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -41,5 +41,61 @@ BOOST_AUTO_TEST_CASE(skiplist_test)
}
}
+BOOST_AUTO_TEST_CASE(getlocator_test)
+{
+ // Build a main chain 100000 blocks long.
+ std::vector<uint256> vHashMain(100000);
+ std::vector<CBlockIndex> vBlocksMain(100000);
+ for (unsigned int i=0; i<vBlocksMain.size(); i++) {
+ vHashMain[i] = i; // Set the hash equal to the height, so we can quickly check the distances.
+ vBlocksMain[i].nHeight = i;
+ vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL;
+ vBlocksMain[i].phashBlock = &vHashMain[i];
+ vBlocksMain[i].BuildSkip();
+ BOOST_CHECK_EQUAL((int)vBlocksMain[i].GetBlockHash().GetLow64(), vBlocksMain[i].nHeight);
+ BOOST_CHECK(vBlocksMain[i].pprev == NULL || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1);
+ }
+
+ // Build a branch that splits off at block 49999, 50000 blocks long.
+ std::vector<uint256> vHashSide(50000);
+ std::vector<CBlockIndex> vBlocksSide(50000);
+ for (unsigned int i=0; i<vBlocksSide.size(); i++) {
+ vHashSide[i] = i + 50000 + (uint256(1) << 128); // Add 1<<128 to the hashes, so GetLow64() still returns the height.
+ vBlocksSide[i].nHeight = i + 50000;
+ vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : &vBlocksMain[49999];
+ vBlocksSide[i].phashBlock = &vHashSide[i];
+ vBlocksSide[i].BuildSkip();
+ BOOST_CHECK_EQUAL((int)vBlocksSide[i].GetBlockHash().GetLow64(), vBlocksSide[i].nHeight);
+ BOOST_CHECK(vBlocksSide[i].pprev == NULL || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1);
+ }
+
+ // Build a CChain for the main branch.
+ CChain chain;
+ chain.SetTip(&vBlocksMain.back());
+
+ // Test 100 random starting points for locators.
+ for (int n=0; n<100; n++) {
+ int r = insecure_rand() % 150000;
+ CBlockIndex* tip = (r < 100000) ? &vBlocksMain[r] : &vBlocksSide[r - 100000];
+ CBlockLocator locator = chain.GetLocator(tip);
+
+ // The first result must be the block itself, the last one must be genesis.
+ BOOST_CHECK(locator.vHave.front() == tip->GetBlockHash());
+ BOOST_CHECK(locator.vHave.back() == vBlocksMain[0].GetBlockHash());
+
+ // Entries 1 through 11 (inclusive) go back one step each.
+ for (unsigned int i = 1; i < 12 && i < locator.vHave.size() - 1; i++) {
+ BOOST_CHECK_EQUAL(locator.vHave[i].GetLow64(), tip->nHeight - i);
+ }
+
+ // The further ones (excluding the last one) go back with exponential steps.
+ unsigned int dist = 2;
+ for (unsigned int i = 12; i < locator.vHave.size() - 1; i++) {
+ BOOST_CHECK_EQUAL(locator.vHave[i - 1].GetLow64() - locator.vHave[i].GetLow64(), dist);
+ dist *= 2;
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 8cae0a4c3..bcd2f75f5 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -18,7 +18,7 @@
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
-
+CClientUIInterface uiInterface;
CWallet* pwalletMain;
extern bool fPrintToConsole;
diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp
index 86a83f516..3887efbd0 100644
--- a/src/test/wallet_tests.cpp
+++ b/src/test/wallet_tests.cpp
@@ -46,7 +46,7 @@ static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, in
wtx->fDebitCached = true;
wtx->nDebitCached = 1;
}
- COutput output(wtx, nInput, nAge);
+ COutput output(wtx, nInput, nAge, true);
vCoins.push_back(output);
}
diff --git a/src/util.cpp b/src/util.cpp
index 5a8f85ade..91ac8833d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -7,7 +7,6 @@
#include "chainparamsbase.h"
#include "sync.h"
-#include "ui_interface.h"
#include "uint256.h"
#include "version.h"
@@ -95,8 +94,8 @@ bool fDaemon = false;
bool fServer = false;
string strMiscWarning;
bool fLogTimestamps = false;
+bool fLogIPs = false;
volatile bool fReopenDebugLog = false;
-CClientUIInterface uiInterface;
// Init OpenSSL library multithreading support
static CCriticalSection** ppmutexOpenSSL;
@@ -122,15 +121,17 @@ public:
CRYPTO_set_locking_callback(locking_callback);
#ifdef WIN32
- // Seed random number generator with screen scrape and other hardware sources
+ // Seed OpenSSL PRNG with current contents of the screen
RAND_screen();
#endif
- // Seed random number generator with performance counter
+ // Seed OpenSSL PRNG with performance counter
RandAddSeed();
}
~CInit()
{
+ // Securely erase the memory used by the PRNG
+ RAND_cleanup();
// Shutdown OpenSSL library multithreading support
CRYPTO_set_locking_callback(NULL);
for (int i = 0; i < CRYPTO_num_locks(); i++)
diff --git a/src/util.h b/src/util.h
index 707b8f2d7..60db71bfd 100644
--- a/src/util.h
+++ b/src/util.h
@@ -100,6 +100,7 @@ extern bool fPrintToDebugLog;
extern bool fServer;
extern std::string strMiscWarning;
extern bool fLogTimestamps;
+extern bool fLogIPs;
extern volatile bool fReopenDebugLog;
void RandAddSeed();
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 318a1388d..a54494f93 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -149,6 +149,21 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return CCryptoKeyStore::AddCScript(redeemScript);
}
+bool CWallet::AddWatchOnly(const CScript &dest)
+{
+ if (!CCryptoKeyStore::AddWatchOnly(dest))
+ return false;
+ nTimeFirstKey = 1; // No birthday information for watch-only keys.
+ if (!fFileBacked)
+ return true;
+ return CWalletDB(strWalletFile).WriteWatchOnly(dest);
+}
+
+bool CWallet::LoadWatchOnly(const CScript &dest)
+{
+ return CCryptoKeyStore::AddWatchOnly(dest);
+}
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
CCrypter crypter;
@@ -515,8 +530,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
{
if (mapBlockIndex.count(wtxIn.hashBlock))
{
- unsigned int latestNow = wtx.nTimeReceived;
- unsigned int latestEntry = 0;
+ int64_t latestNow = wtx.nTimeReceived;
+ int64_t latestEntry = 0;
{
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
@@ -547,7 +562,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
}
}
- unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime;
+ int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
else
@@ -684,7 +699,7 @@ void CWallet::EraseFromWallet(const uint256 &hash)
}
-bool CWallet::IsMine(const CTxIn &txin) const
+isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
@@ -693,14 +708,13 @@ bool CWallet::IsMine(const CTxIn &txin) const
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
- if (IsMine(prev.vout[txin.prevout.n]))
- return true;
+ return IsMine(prev.vout[txin.prevout.n]);
}
}
- return false;
+ return ISMINE_NO;
}
-int64_t CWallet::GetDebit(const CTxIn &txin) const
+int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
LOCK(cs_wallet);
@@ -709,7 +723,7 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
- if (IsMine(prev.vout[txin.prevout.n]))
+ if (IsMine(prev.vout[txin.prevout.n]) & filter)
return prev.vout[txin.prevout.n].nValue;
}
}
@@ -718,17 +732,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const
bool CWallet::IsChange(const CTxOut& txout) const
{
- CTxDestination address;
-
// TODO: fix handling of 'change' outputs. The assumption is that any
- // payment to a TX_PUBKEYHASH that is mine but isn't in the address book
+ // payment to a script that is ours, but is not in the address book
// is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address;
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
- if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address))
+ if (::IsMine(*this, txout.scriptPubKey))
{
+ CTxDestination address;
+ if (!ExtractDestination(txout.scriptPubKey, address))
+ return true;
+
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
return true;
@@ -782,7 +798,7 @@ int CWalletTx::GetRequestCount() const
}
void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
- list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount) const
+ list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const
{
nFee = 0;
listReceived.clear();
@@ -790,7 +806,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
strSentAccount = strFromAccount;
// Compute fee:
- int64_t nDebit = GetDebit();
+ int64_t nDebit = GetDebit(filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
int64_t nValueOut = GetValueOut();
@@ -800,7 +816,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
// Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
- bool fIsMine;
+ isminetype fIsMine = pwallet->IsMine(txout);
+
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
@@ -809,9 +826,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
// Don't report 'change' txouts
if (pwallet->IsChange(txout))
continue;
- fIsMine = pwallet->IsMine(txout);
}
- else if (!(fIsMine = pwallet->IsMine(txout)))
+ else if (!(fIsMine & filter))
continue;
// In either case, we need to get the destination address
@@ -835,7 +851,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
}
void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
- int64_t& nSent, int64_t& nFee) const
+ int64_t& nSent, int64_t& nFee, const isminefilter& filter) const
{
nReceived = nSent = nFee = 0;
@@ -843,7 +859,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
- GetAmounts(listReceived, listSent, allFee, strSentAccount);
+ GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (strAccount == strSentAccount)
{
@@ -889,7 +905,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
- while (pindex && nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200)))
+ while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200)))
pindex = chainActive.Next(pindex);
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
@@ -1055,7 +1071,52 @@ int64_t CWallet::GetImmatureBalance() const
return nTotal;
}
-// populate vCoins with vector of spendable COutputs
+int64_t CWallet::GetWatchOnlyBalance() const
+{
+ int64_t nTotal = 0;
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsTrusted())
+ nTotal += pcoin->GetAvailableWatchOnlyCredit();
+ }
+ }
+
+ return nTotal;
+}
+
+int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const
+{
+ int64_t nTotal = 0;
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
+ nTotal += pcoin->GetAvailableWatchOnlyCredit();
+ }
+ }
+ return nTotal;
+}
+
+int64_t CWallet::GetImmatureWatchOnlyBalance() const
+{
+ int64_t nTotal = 0;
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ nTotal += pcoin->GetImmatureWatchOnlyCredit();
+ }
+ }
+ return nTotal;
+}
+
+// populate vCoins with vector of available COutputs.
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
{
vCoins.clear();
@@ -1081,10 +1142,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) &&
+ isminetype mine = IsMine(pcoin->vout[i]);
+ if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
- vCoins.push_back(COutput(pcoin, i, nDepth));
+ vCoins.push_back(COutput(pcoin, i, nDepth, mine & ISMINE_SPENDABLE));
}
}
}
@@ -1151,11 +1213,14 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
- BOOST_FOREACH(COutput output, vCoins)
+ BOOST_FOREACH(const COutput &output, vCoins)
{
+ if (!output.fSpendable)
+ continue;
+
const CWalletTx *pcoin = output.tx;
- if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
+ if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
int i = output.i;
@@ -1244,6 +1309,8 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign
{
BOOST_FOREACH(const COutput& out, vCoins)
{
+ if(!out.fSpendable)
+ continue;
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
@@ -1417,7 +1484,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
break;
// Small enough, and priority high enough, to send for free
- if (dPriority >= dPriorityNeeded)
+ if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
break;
// Include more fee and try again.
@@ -1487,18 +1554,29 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
-string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew)
+string CWallet::SendMoney(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew)
{
- CReserveKey reservekey(this);
- int64_t nFeeRequired;
+ // Check amount
+ if (nValue <= 0)
+ return _("Invalid amount");
+ if (nValue > GetBalance())
+ return _("Insufficient funds");
+ string strError;
if (IsLocked())
{
- string strError = _("Error: Wallet locked, unable to create transaction!");
+ strError = _("Error: Wallet locked, unable to create transaction!");
LogPrintf("SendMoney() : %s", strError);
return strError;
}
- string strError;
+
+ // Parse Bitcoin address
+ CScript scriptPubKey;
+ scriptPubKey.SetDestination(address);
+
+ // Create and send the transaction
+ CReserveKey reservekey(this);
+ int64_t nFeeRequired;
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError))
{
if (nValue + nFeeRequired > GetBalance())
@@ -1506,7 +1584,6 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe
LogPrintf("SendMoney() : %s\n", strError);
return strError;
}
-
if (!CommitTransaction(wtxNew, reservekey))
return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
@@ -1515,21 +1592,6 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe
-string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew)
-{
- // Check amount
- if (nValue <= 0)
- return _("Invalid amount");
- if (nValue > GetBalance())
- return _("Insufficient funds");
-
- // Parse Bitcoin address
- CScript scriptPubKey;
- scriptPubKey.SetDestination(address);
-
- return SendMoney(scriptPubKey, nValue, wtxNew);
-}
-
int64_t CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
{
// payTxFee is user-set "I want to pay this much"
@@ -1809,7 +1871,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances()
continue;
int nDepth = pcoin->GetDepthInMainChain();
- if (nDepth < (pcoin->IsFromMe() ? 0 : 1))
+ if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
@@ -2092,7 +2154,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
// Extract block timestamps for those keys
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
- mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
+ mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off
}
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
diff --git a/src/wallet.h b/src/wallet.h
index a5162bb83..ab9550a98 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -229,6 +229,11 @@ public:
/// Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
+ // Adds a watch-only address to the store, and saves it to disk.
+ bool AddWatchOnly(const CScript &dest);
+ // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
+ bool LoadWatchOnly(const CScript &dest);
+
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
@@ -260,13 +265,15 @@ public:
int64_t GetBalance() const;
int64_t GetUnconfirmedBalance() const;
int64_t GetImmatureBalance() const;
+ int64_t GetWatchOnlyBalance() const;
+ int64_t GetUnconfirmedWatchOnlyBalance() const;
+ int64_t GetImmatureWatchOnlyBalance() const;
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CreateTransaction(CScript scriptPubKey, int64_t nValue,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
- std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew);
- std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew);
+ std::string SendMoney(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew);
static CFeeRate minTxFee;
static int64_t GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
@@ -285,17 +292,17 @@ public:
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
- bool IsMine(const CTxIn& txin) const;
- int64_t GetDebit(const CTxIn& txin) const;
- bool IsMine(const CTxOut& txout) const
+ isminetype IsMine(const CTxIn& txin) const;
+ int64_t GetDebit(const CTxIn& txin, const isminefilter& filter) const;
+ isminetype IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
- int64_t GetCredit(const CTxOut& txout) const
+ int64_t GetCredit(const CTxOut& txout, const isminefilter& filter) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
- return (IsMine(txout) ? txout.nValue : 0);
+ return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
bool IsChange(const CTxOut& txout) const;
int64_t GetChange(const CTxOut& txout) const
@@ -311,9 +318,9 @@ public:
return true;
return false;
}
- bool IsFromMe(const CTransaction& tx) const
+ bool IsFromMe(const CTransaction& tx) const // should probably be renamed to IsRelevantToMe
{
- return (GetDebit(tx) > 0);
+ return (GetDebit(tx, ISMINE_ALL) > 0);
}
bool IsConflicting(const CTransaction& tx) const
{
@@ -322,23 +329,23 @@ public:
return true;
return false;
}
- int64_t GetDebit(const CTransaction& tx) const
+ int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const
{
int64_t nDebit = 0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
- nDebit += GetDebit(txin);
+ nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit))
throw std::runtime_error("CWallet::GetDebit() : value out of range");
}
return nDebit;
}
- int64_t GetCredit(const CTransaction& tx) const
+ int64_t GetCredit(const CTransaction& tx, const isminefilter& filter) const
{
int64_t nCredit = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
- nCredit += GetCredit(txout);
+ nCredit += GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWallet::GetCredit() : value out of range");
}
@@ -484,11 +491,19 @@ public:
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
+ mutable bool fWatchDebitCached;
+ mutable bool fWatchCreditCached;
+ mutable bool fImmatureWatchCreditCached;
+ mutable bool fAvailableWatchCreditCached;
mutable bool fChangeCached;
mutable int64_t nDebitCached;
mutable int64_t nCreditCached;
mutable int64_t nImmatureCreditCached;
mutable int64_t nAvailableCreditCached;
+ mutable int64_t nWatchDebitCached;
+ mutable int64_t nWatchCreditCached;
+ mutable int64_t nImmatureWatchCreditCached;
+ mutable int64_t nAvailableWatchCreditCached;
mutable int64_t nChangeCached;
CWalletTx()
@@ -525,11 +540,19 @@ public:
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
+ fWatchDebitCached = false;
+ fWatchCreditCached = false;
+ fImmatureWatchCreditCached = false;
+ fAvailableWatchCreditCached = false;
fChangeCached = false;
nDebitCached = 0;
nCreditCached = 0;
nImmatureCreditCached = 0;
nAvailableCreditCached = 0;
+ nWatchDebitCached = 0;
+ nWatchCreditCached = 0;
+ nAvailableWatchCreditCached = 0;
+ nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
@@ -582,6 +605,10 @@ public:
{
fCreditCached = false;
fAvailableCreditCached = false;
+ fWatchDebitCached = false;
+ fWatchCreditCached = false;
+ fAvailableWatchCreditCached = false;
+ fImmatureWatchCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
@@ -592,15 +619,36 @@ public:
MarkDirty();
}
- int64_t GetDebit() const
+ // filter decides which addresses will count towards the debit
+ int64_t GetDebit(const isminefilter& filter) const
{
if (vin.empty())
return 0;
- if (fDebitCached)
- return nDebitCached;
- nDebitCached = pwallet->GetDebit(*this);
- fDebitCached = true;
- return nDebitCached;
+
+ int64_t debit = 0;
+ if(filter & ISMINE_SPENDABLE)
+ {
+ if (fDebitCached)
+ debit += nDebitCached;
+ else
+ {
+ nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
+ fDebitCached = true;
+ debit += nDebitCached;
+ }
+ }
+ if(filter & ISMINE_WATCH_ONLY)
+ {
+ if(fWatchDebitCached)
+ debit += nWatchDebitCached;
+ else
+ {
+ nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
+ fWatchDebitCached = true;
+ debit += nWatchDebitCached;
+ }
+ }
+ return debit;
}
int64_t GetCredit(bool fUseCache=true) const
@@ -612,7 +660,7 @@ public:
// GetBalance can assume transactions in mapWallet won't change
if (fUseCache && fCreditCached)
return nCreditCached;
- nCreditCached = pwallet->GetCredit(*this);
+ nCreditCached = pwallet->GetCredit(*this, ISMINE_ALL);
fCreditCached = true;
return nCreditCached;
}
@@ -623,7 +671,7 @@ public:
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*this);
+ nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
@@ -650,7 +698,7 @@ public:
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = vout[i];
- nCredit += pwallet->GetCredit(txout);
+ nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
@@ -661,6 +709,48 @@ public:
return nCredit;
}
+ int64_t GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const
+ {
+ if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
+ {
+ if (fUseCache && fImmatureWatchCreditCached)
+ return nImmatureWatchCreditCached;
+ nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
+ fImmatureWatchCreditCached = true;
+ return nImmatureWatchCreditCached;
+ }
+
+ return 0;
+ }
+
+ int64_t GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const
+ {
+ if (pwallet == 0)
+ return 0;
+
+ // Must wait until coinbase is safely deep enough in the chain before valuing it
+ if (IsCoinBase() && GetBlocksToMaturity() > 0)
+ return 0;
+
+ if (fUseCache && fAvailableWatchCreditCached)
+ return nAvailableWatchCreditCached;
+
+ int64_t nCredit = 0;
+ for (unsigned int i = 0; i < vout.size(); i++)
+ {
+ if (!pwallet->IsSpent(GetHash(), i))
+ {
+ const CTxOut &txout = vout[i];
+ nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
+ if (!MoneyRange(nCredit))
+ throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
+ }
+ }
+
+ nAvailableWatchCreditCached = nCredit;
+ fAvailableWatchCreditCached = true;
+ return nCredit;
+ }
int64_t GetChange() const
{
@@ -672,14 +762,14 @@ public:
}
void GetAmounts(std::list<std::pair<CTxDestination, int64_t> >& listReceived,
- std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount) const;
+ std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const;
void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived,
- int64_t& nSent, int64_t& nFee) const;
+ int64_t& nSent, int64_t& nFee, const isminefilter& filter) const;
- bool IsFromMe() const
+ bool IsFromMe(const isminefilter& filter) const
{
- return (GetDebit() > 0);
+ return (GetDebit(filter) > 0);
}
bool IsTrusted() const
@@ -692,7 +782,7 @@ public:
return true;
if (nDepth < 0)
return false;
- if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit
+ if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
return false;
// Trusted if all inputs are from us and are in the mempool:
@@ -703,7 +793,7 @@ public:
if (parent == NULL)
return false;
const CTxOut& parentOut = parent->vout[txin.prevout.n];
- if (!pwallet->IsMine(parentOut))
+ if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
return false;
}
return true;
@@ -728,10 +818,11 @@ public:
const CWalletTx *tx;
int i;
int nDepth;
+ bool fSpendable;
- COutput(const CWalletTx *txIn, int iIn, int nDepthIn)
+ COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn)
{
- tx = txIn; i = iIn; nDepth = nDepthIn;
+ tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn;
}
std::string ToString() const
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 3ce2ef019..a95baf83d 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -112,6 +112,12 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
+bool CWalletDB::WriteWatchOnly(const CScript &dest)
+{
+ nWalletDBUpdated++;
+ return Write(std::make_pair(std::string("watchs"), dest), '1');
+}
+
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
@@ -404,6 +410,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fAnyUnordered = true;
}
}
+ else if (strType == "watchs")
+ {
+ CScript script;
+ ssKey >> script;
+ char fYes;
+ ssValue >> fYes;
+ if (fYes == '1')
+ pwallet->LoadWatchOnly(script);
+
+ // Watch-only addresses have no birthday information for now,
+ // so set the wallet birthday to the beginning of time.
+ pwallet->nTimeFirstKey = 1;
+ }
else if (strType == "key" || strType == "wkey")
{
CPubKey vchPubKey;
diff --git a/src/walletdb.h b/src/walletdb.h
index 8eb716acb..58b4571b1 100644
--- a/src/walletdb.h
+++ b/src/walletdb.h
@@ -7,6 +7,7 @@
#include "db.h"
#include "key.h"
+#include "keystore.h"
#include <list>
#include <stdint.h>
@@ -93,6 +94,8 @@ public:
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
+ bool WriteWatchOnly(const CScript &script);
+
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);