aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/debian/control5
-rw-r--r--contrib/debian/manpages/bitcoin.conf.52
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus3
-rwxr-xr-xqa/pull-tester/run-bitcoind-for-test.sh.in2
-rw-r--r--qa/rpc-tests/README.md4
-rwxr-xr-xqa/rpc-tests/getblocktemplate.py94
-rwxr-xr-xqa/rpc-tests/keypool.py132
-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.py19
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/init.cpp66
-rw-r--r--src/leveldbwrapper.cpp5
-rw-r--r--src/main.cpp24
-rw-r--r--src/main.h2
-rw-r--r--src/miner.cpp3
-rw-r--r--src/net.cpp60
-rw-r--r--src/net.h13
-rw-r--r--src/qt/bitcoingui.cpp25
-rw-r--r--src/qt/rpcconsole.cpp29
-rw-r--r--src/qt/rpcconsole.h2
-rw-r--r--src/rpcblockchain.cpp4
-rw-r--r--src/rpcmining.cpp60
-rw-r--r--src/rpcnet.cpp1
-rw-r--r--src/rpcrawtransaction.cpp2
-rw-r--r--src/rpcserver.cpp49
-rw-r--r--src/rpcserver.h2
-rw-r--r--src/rpcwallet.cpp2
-rw-r--r--src/sync.h3
-rw-r--r--src/ui_interface.h2
-rw-r--r--src/wallet.cpp12
35 files changed, 882 insertions, 610 deletions
diff --git a/contrib/debian/control b/contrib/debian/control
index a04e88d4e1..ac635f43e0 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/contrib/debian/manpages/bitcoin.conf.5 b/contrib/debian/manpages/bitcoin.conf.5
index 7438b4b66a..8a0078d5d5 100644
--- a/contrib/debian/manpages/bitcoin.conf.5
+++ b/contrib/debian/manpages/bitcoin.conf.5
@@ -2,7 +2,7 @@
.SH NAME
bitcoin.conf \- bitcoin configuration file
.SH SYNOPSIS
-All command-line options (except for '\-datadir' and '\-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file.
+All command-line options (except for '\-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file.
.TP
The configuration file is a list of 'setting=value' pairs, one per line, with optional comments starting with the '#' character.
.TP
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index ce4169a410..fdad28018f 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -211,6 +211,7 @@ def getFrameworks(binaryPath, verbose):
libraries = []
for line in otoolLines:
+ line = line.replace("@loader_path", os.path.dirname(binaryPath))
info = FrameworkInfo.fromOtoolLibraryLine(line.strip())
if info is not None:
if verbose >= 3:
@@ -307,7 +308,7 @@ def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploym
if deploymentInfo.qtPath is None and framework.isQtFramework():
deploymentInfo.detectQtPath(framework.frameworkDirectory)
- if framework.installName.startswith("@executable_path"):
+ if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath):
if verbose >= 2:
print framework.frameworkName, "already deployed, skipping."
continue
diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in
index 391046ab85..ecc42e12b1 100755
--- a/qa/pull-tester/run-bitcoind-for-test.sh.in
+++ b/qa/pull-tester/run-bitcoind-for-test.sh.in
@@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
WAITER=$!
PORT=`expr $BASHPID + 10000`
-"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -regtest -rpcport=`expr $PORT + 1` &
+"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
BITCOIND=$!
#Install a watchdog.
diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md
index 616c0525f4..3e916a7688 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/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py
new file mode 100755
index 0000000000..8d97719ec3
--- /dev/null
+++ b/qa/rpc-tests/getblocktemplate.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The Bitcoin Core developers
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+# Exercise the listtransactions API
+
+from test_framework import BitcoinTestFramework
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+
+def check_array_result(object_array, to_match, expected):
+ """
+ Pass in array of JSON objects, a dictionary with key/value pairs
+ to match against, and another dictionary with expected key/value
+ pairs.
+ """
+ num_matched = 0
+ for item in object_array:
+ all_match = True
+ for key,value in to_match.items():
+ if item[key] != value:
+ all_match = False
+ if not all_match:
+ continue
+ for key,value in expected.items():
+ if item[key] != value:
+ raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
+ num_matched = num_matched+1
+ if num_matched == 0:
+ raise AssertionError("No objects matched %s"%(str(to_match)))
+
+import threading
+
+class LongpollThread(threading.Thread):
+ def __init__(self, node):
+ threading.Thread.__init__(self)
+ # query current longpollid
+ templat = node.getblocktemplate()
+ self.longpollid = templat['longpollid']
+ # create a new connection to the node, we can't use the same
+ # connection from two threads
+ self.node = AuthServiceProxy(node.url, timeout=600)
+
+ def run(self):
+ self.node.getblocktemplate({'longpollid':self.longpollid})
+
+class GetBlockTemplateTest(BitcoinTestFramework):
+ '''
+ Test longpolling with getblocktemplate.
+ '''
+
+ def run_test(self, nodes):
+ print "Warning: this test will take about 70 seconds in the best case. Be patient."
+ nodes[0].setgenerate(True, 10)
+ templat = nodes[0].getblocktemplate()
+ longpollid = templat['longpollid']
+ # longpollid should not change between successive invocations if nothing else happens
+ templat2 = nodes[0].getblocktemplate()
+ assert(templat2['longpollid'] == longpollid)
+
+ # Test 1: test that the longpolling wait if we do nothing
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ # check that thread still lives
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(thr.is_alive())
+
+ # Test 2: test that longpoll will terminate if another node generates a block
+ nodes[1].setgenerate(True, 1) # generate a block on another node
+ # check that thread will exit now that new transaction entered mempool
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(not thr.is_alive())
+
+ # Test 3: test that longpoll will terminate if we generate a block ourselves
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ nodes[0].setgenerate(True, 1) # generate a block on another node
+ thr.join(5) # wait 5 seconds or until thread exits
+ assert(not thr.is_alive())
+
+ # Test 4: test that introducing a new transaction into the mempool will terminate the longpoll
+ thr = LongpollThread(nodes[0])
+ thr.start()
+ # generate a random transaction and submit it
+ (txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"), Decimal("0.0"), Decimal("0.001"), 20)
+ # after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned
+ thr.join(60 + 20)
+ assert(not thr.is_alive())
+
+if __name__ == '__main__':
+ GetBlockTemplateTest().main()
+
diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py
new file mode 100755
index 0000000000..86ad20de52
--- /dev/null
+++ b/qa/rpc-tests/keypool.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The Bitcoin Core developers
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+# Exercise the wallet keypool, and interaction with wallet encryption/locking
+
+# 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 check_array_result(object_array, to_match, expected):
+ """
+ Pass in array of JSON objects, a dictionary with key/value pairs
+ to match against, and another dictionary with expected key/value
+ pairs.
+ """
+ num_matched = 0
+ for item in object_array:
+ all_match = True
+ for key,value in to_match.items():
+ if item[key] != value:
+ all_match = False
+ if not all_match:
+ continue
+ for key,value in expected.items():
+ if item[key] != value:
+ raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
+ num_matched = num_matched+1
+ if num_matched == 0:
+ raise AssertionError("No objects matched %s"%(str(to_match)))
+
+def run_test(nodes, tmpdir):
+ # Encrypt wallet and wait to terminate
+ nodes[0].encryptwallet('test')
+ bitcoind_processes[0].wait()
+ # Restart node 0
+ nodes[0] = start_node(0, tmpdir)
+ # Keep creating keys
+ addr = nodes[0].getnewaddress()
+ try:
+ addr = nodes[0].getnewaddress()
+ raise AssertionError('Keypool should be exhausted after one address')
+ except JSONRPCException,e:
+ assert(e.error['code']==-12)
+
+ # put three new keys in the keypool
+ nodes[0].walletpassphrase('test', 12000)
+ nodes[0].keypoolrefill(3)
+ nodes[0].walletlock()
+
+ # drain the keys
+ addr = set()
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ # assert that four unique addresses were returned
+ assert(len(addr) == 4)
+ # the next one should fail
+ try:
+ addr = nodes[0].getrawchangeaddress()
+ raise AssertionError('Keypool should be exhausted after three addresses')
+ except JSONRPCException,e:
+ assert(e.error['code']==-12)
+
+
+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(1, options.tmpdir)
+
+ run_test(nodes, options.tmpdir)
+
+ success = True
+
+ except AssertionError as e:
+ print("Assertion failed: "+e.message)
+ except JSONRPCException as e:
+ print("JSONRPC error: "+e.error['message'])
+ traceback.print_tb(sys.exc_info()[2])
+ except Exception as e:
+ print("Unexpected exception caught during testing: "+str(sys.exc_info()[0]))
+ 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/listtransactions.py b/qa/rpc-tests/listtransactions.py
index f16095c125..50385b4372 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 c2e5406c2f..bc7d655fdf 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 7f2d79b3c1..61f5e0452b 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 126b6bfaf4..0000000000
--- 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 e8abbfba19..352a1de2d0 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 0000000000..5a18556655
--- /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 27c9f778f6..fc7ae85776 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,14 +149,16 @@ 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) +
["-rpcwait", "getblockcount"], stdout=devnull)
devnull.close()
url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
- return AuthServiceProxy(url)
+ proxy = AuthServiceProxy(url)
+ proxy.url = url # store URL on proxy for info
+ return proxy
def start_nodes(num_nodes, dir, extra_args=None, rpchost=None):
"""
@@ -168,6 +170,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 +182,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/src/chainparams.cpp b/src/chainparams.cpp
index 50977dc06b..fb1d05f833 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -152,6 +152,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/init.cpp b/src/init.cpp
index f9e568a207..99df237b2a 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;
@@ -58,7 +58,8 @@ CWallet* pwalletMain;
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
- BF_REPORT_ERROR = (1U << 1)
+ BF_REPORT_ERROR = (1U << 1),
+ BF_WHITELIST = (1U << 2),
};
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
@@ -109,10 +110,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);
@@ -130,7 +132,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);
@@ -142,9 +144,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)
@@ -156,7 +161,7 @@ void Shutdown()
if (pwalletMain)
delete pwalletMain;
#endif
- LogPrintf("Shutdown : done\n");
+ LogPrintf("%s: done\n", __func__);
}
//
@@ -174,13 +179,13 @@ void HandleSIGHUP(int)
bool static InitError(const std::string &str)
{
- uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::NOSHOWGUI);
+ uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR);
return false;
}
bool static InitWarning(const std::string &str)
{
- uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING | CClientUIInterface::NOSHOWGUI);
+ uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
return true;
}
@@ -188,7 +193,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError)) {
+ if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) {
if (flags & BF_REPORT_ERROR)
return InitError(strError);
return false;
@@ -214,7 +219,6 @@ std::string HelpMessage(HelpMessageMode mode)
}
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n";
- strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n";
strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n";
strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n";
@@ -249,10 +253,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
#endif
#endif
+ strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
+ strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n";
+ strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
if (GetBoolArg("-help-debug", false))
strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n";
strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n";
@@ -288,8 +295,10 @@ std::string HelpMessage(HelpMessageMode mode)
if (mode == HMM_BITCOIN_QT)
strUsage += ", qt";
strUsage += ".\n";
+#ifdef ENABLE_WALLET
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n";
+#endif
strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n";
strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n";
strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n";
@@ -315,6 +324,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";
@@ -499,11 +509,11 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 2: parameter interactions
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (SoftSetBoolArg("-listen", true))
- LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n");
+ LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
}
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@@ -547,7 +557,7 @@ bool AppInit2(boost::thread_group& threadGroup)
}
// Make sure enough file descriptors are available
- int nBind = std::max((int)mapArgs.count("-bind"), 1);
+ int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@@ -564,9 +574,9 @@ 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."));
@@ -764,6 +774,15 @@ bool AppInit2(boost::thread_group& threadGroup)
}
}
+ if (mapArgs.count("-whitelist")) {
+ BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
+ CSubNet subnet(net);
+ if (!subnet.IsValid())
+ return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
+ CNode::AddWhitelistedRange(subnet);
+ }
+ }
+
CService addrProxy;
bool fProxy = false;
if (mapArgs.count("-proxy")) {
@@ -800,13 +819,21 @@ bool AppInit2(boost::thread_group& threadGroup)
bool fBound = false;
if (fListen) {
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
+ BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false))
+ return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
+ if (addrBind.GetPort() == 0)
+ return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ }
}
else {
struct in_addr inaddr_any;
@@ -994,6 +1021,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);
diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp
index 5b4a9c147b..9e849696a8 100644
--- a/src/leveldbwrapper.cpp
+++ b/src/leveldbwrapper.cpp
@@ -32,6 +32,11 @@ static leveldb::Options GetOptions(size_t nCacheSize) {
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.compression = leveldb::kNoCompression;
options.max_open_files = 64;
+ if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
+ // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error
+ // on corruption in later versions.
+ options.paranoid_checks = true;
+ }
return options;
}
diff --git a/src/main.cpp b/src/main.cpp
index 8225d73d8d..4f6b442f3a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -41,6 +41,8 @@ CCriticalSection cs_main;
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
int64_t nTimeBestReceived = 0;
+CWaitableCriticalSection csBestBlock;
+CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
@@ -210,7 +212,7 @@ struct CBlockReject {
struct CNodeState {
// Accumulated misbehaviour score for this peer.
int nMisbehavior;
- // Whether this peer should be disconnected and banned.
+ // Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan;
// String name of this peer (debugging/logging purposes).
std::string name;
@@ -1425,7 +1427,8 @@ void Misbehaving(NodeId pnode, int howmuch)
return;
state->nMisbehavior += howmuch;
- if (state->nMisbehavior >= GetArg("-banscore", 100))
+ int banscore = GetArg("-banscore", 100);
+ if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
state->fShouldBan = true;
@@ -1944,11 +1947,14 @@ void static UpdateTip(CBlockIndex *pindexNew) {
// New best block
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
+
LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip()));
+ cvBlockChange.notify_all();
+
// Check the version of the last 100 blocks to see if we need to upgrade:
if (!fIsInitialDownload)
{
@@ -3947,6 +3953,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
+ } else if (pfrom->fWhitelisted) {
+ // Always relay transactions received from whitelisted peers, even
+ // if they are already in the mempool (allowing the node to function
+ // as a gateway for nodes hidden behind it).
+ RelayTransaction(tx);
}
int nDoS = 0;
if (state.IsInvalid(nDoS))
@@ -4440,11 +4451,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
- if (pto->addr.IsLocal())
- LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
+ if (pto->fWhitelisted)
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
else {
pto->fDisconnect = true;
- CNode::Ban(pto->addr);
+ if (pto->addr.IsLocal())
+ LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
+ else
+ CNode::Ban(pto->addr);
}
state.fShouldBan = false;
}
diff --git a/src/main.h b/src/main.h
index f6bac889be..a683412571 100644
--- a/src/main.h
+++ b/src/main.h
@@ -87,6 +87,8 @@ extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern const std::string strMessageMagic;
extern int64_t nTimeBestReceived;
+extern CWaitableCriticalSection csBestBlock;
+extern CConditionVariable cvBlockChange;
extern bool fImporting;
extern bool fReindex;
extern bool fBenchmark;
diff --git a/src/miner.cpp b/src/miner.cpp
index 17918a1280..ec56c71192 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -467,7 +467,10 @@ void static BitcoinMiner(CWallet *pwallet)
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
if (!pblocktemplate.get())
+ {
+ LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
return;
+ }
CBlock *pblock = &pblocktemplate->block;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
diff --git a/src/net.cpp b/src/net.cpp
index 0e663aea83..3b3d91d652 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -50,7 +50,16 @@
using namespace std;
using namespace boost;
-static const int MAX_OUTBOUND_CONNECTIONS = 8;
+namespace {
+ const int MAX_OUTBOUND_CONNECTIONS = 8;
+
+ struct ListenSocket {
+ SOCKET socket;
+ bool whitelisted;
+
+ ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
+ };
+}
//
// Global state variables
@@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
static CNode* pnodeSync = NULL;
uint64_t nLocalHostNonce = 0;
-static std::vector<SOCKET> vhListenSocket;
+static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
@@ -593,6 +602,24 @@ bool CNode::Ban(const CNetAddr &addr) {
return true;
}
+
+std::vector<CSubNet> CNode::vWhitelistedRange;
+CCriticalSection CNode::cs_vWhitelistedRange;
+
+bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
+ LOCK(cs_vWhitelistedRange);
+ BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
+ if (subnet.Match(addr))
+ return true;
+ }
+ return false;
+}
+
+void CNode::AddWhitelistedRange(const CSubNet &subnet) {
+ LOCK(cs_vWhitelistedRange);
+ vWhitelistedRange.push_back(subnet);
+}
+
#undef X
#define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats)
@@ -609,6 +636,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nStartingHeight);
X(nSendBytes);
X(nRecvBytes);
+ X(fWhitelisted);
stats.fSyncNode = (this == pnodeSync);
// It is common for nodes with good ping times to suddenly become lagged,
@@ -848,9 +876,9 @@ void ThreadSocketHandler()
SOCKET hSocketMax = 0;
bool have_fds = false;
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) {
- FD_SET(hListenSocket, &fdsetRecv);
- hSocketMax = max(hSocketMax, hListenSocket);
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
+ FD_SET(hListenSocket.socket, &fdsetRecv);
+ hSocketMax = max(hSocketMax, hListenSocket.socket);
have_fds = true;
}
@@ -917,13 +945,13 @@ void ThreadSocketHandler()
//
// Accept new connections
//
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
{
- if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
+ if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
+ SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
@@ -931,6 +959,7 @@ void ThreadSocketHandler()
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n");
+ bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -948,7 +977,7 @@ void ThreadSocketHandler()
{
closesocket(hSocket);
}
- else if (CNode::IsBanned(addr))
+ else if (CNode::IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
closesocket(hSocket);
@@ -957,6 +986,7 @@ void ThreadSocketHandler()
{
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
+ pnode->fWhitelisted = whitelisted;
{
LOCK(cs_vNodes);
@@ -1580,7 +1610,7 @@ void ThreadMessageHandler()
-bool BindListenPort(const CService &addrBind, string& strError)
+bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
{
strError = "";
int nOne = 1;
@@ -1661,9 +1691,9 @@ bool BindListenPort(const CService &addrBind, string& strError)
return false;
}
- vhListenSocket.push_back(hListenSocket);
+ vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
- if (addrBind.IsRoutable() && fDiscover)
+ if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -1788,9 +1818,9 @@ public:
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET)
closesocket(pnode->hSocket);
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
- if (hListenSocket != INVALID_SOCKET)
- if (closesocket(hListenSocket) == SOCKET_ERROR)
+ BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
+ if (hListenSocket.socket != INVALID_SOCKET)
+ if (closesocket(hListenSocket.socket) == SOCKET_ERROR)
LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection)
diff --git a/src/net.h b/src/net.h
index 21a2919f4a..866c9ae783 100644
--- a/src/net.h
+++ b/src/net.h
@@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
void MapPort(bool fUseUPnP);
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError);
+bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
void StartNode(boost::thread_group& threadGroup);
bool StopNode();
void SocketSendData(CNode *pnode);
@@ -154,6 +154,7 @@ public:
uint64_t nSendBytes;
uint64_t nRecvBytes;
bool fSyncNode;
+ bool fWhitelisted;
double dPingTime;
double dPingWait;
std::string addrLocal;
@@ -236,6 +237,7 @@ public:
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
+ bool fWhitelisted; // This peer can bypass DoS banning.
bool fOneShot;
bool fClient;
bool fInbound;
@@ -259,6 +261,11 @@ protected:
static std::map<CNetAddr, int64_t> setBanned;
static CCriticalSection cs_setBanned;
+ // Whitelisted ranges. Any node connecting from these is automatically
+ // whitelisted (as well as those connecting to whitelisted binds).
+ static std::vector<CSubNet> vWhitelistedRange;
+ static CCriticalSection cs_vWhitelistedRange;
+
// Basic fuzz-testing
void Fuzz(int nChance); // modifies ssSend
@@ -305,6 +312,7 @@ public:
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
strSubVer = "";
+ fWhitelisted = false;
fOneShot = false;
fClient = false; // set by version message
fInbound = fInboundIn;
@@ -720,6 +728,9 @@ public:
static bool Ban(const CNetAddr &ip);
void copyStats(CNodeStats &stats);
+ static bool IsWhitelistedRange(const CNetAddr &ip);
+ static void AddWhitelistedRange(const CSubNet &subnet);
+
// Network stats
static void RecordBytesRecv(uint64_t bytes);
static void RecordBytesSent(uint64_t bytes);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6b3aa2a2df..5fc2f500b5 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -159,10 +159,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
labelEncryptionIcon = new QLabel();
labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel();
- frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(unitDisplayControl);
- frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(labelEncryptionIcon);
+ if(enableWallet)
+ {
+ frameBlocksLayout->addStretch();
+ frameBlocksLayout->addWidget(unitDisplayControl);
+ frameBlocksLayout->addStretch();
+ frameBlocksLayout->addWidget(labelEncryptionIcon);
+ }
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon);
frameBlocksLayout->addStretch();
@@ -780,11 +783,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
buttons = QMessageBox::Ok;
- // Ensure we get users attention, but only if main window is visible
- // as we don't want to pop up the main window for messages that happen before
- // initialization is finished.
- if(!(style & CClientUIInterface::NOSHOWGUI))
- showNormalIfMinimized();
+ showNormalIfMinimized();
QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this);
int r = mBox.exec();
if (ret != NULL)
@@ -921,6 +920,8 @@ void BitcoinGUI::setEncryptionStatus(int status)
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
{
+ if(!clientModel)
+ return;
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (isHidden())
{
@@ -1012,7 +1013,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
}
-/** So that it responds to left-button clicks */
+/** So that it responds to button clicks */
void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
{
onDisplayUnitsClicked(event->pos());
@@ -1029,10 +1030,6 @@ void UnitDisplayStatusBarControl::createContextMenu()
menu->addAction(menuAction);
}
connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*)));
-
- // what happens on right click.
- setContextMenuPolicy(Qt::CustomContextMenu);
- connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&)));
}
/** Lets the control know about the Options Model (and its signals) */
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index e1f40ddd09..9b67f8125f 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -473,6 +473,10 @@ void RPCConsole::on_tabWidget_currentChanged(int index)
{
ui->lineEdit->setFocus();
}
+ else if(ui->tabWidget->widget(index) == ui->tab_peers)
+ {
+ initPeerTable();
+ }
}
void RPCConsole::on_openDebugLogfileButton_clicked()
@@ -648,11 +652,27 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats)
ui->peerBanScore->setText(tr("Fetching..."));
}
+void RPCConsole::initPeerTable()
+{
+ if (!clientModel)
+ return;
+
+ // peerWidget needs a resize in case the dialog has non-default geometry
+ columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
+
+ // start PeerTableModel auto refresh
+ clientModel->getPeerTableModel()->startAutoRefresh(1000);
+}
+
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void RPCConsole::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
+
+ if (!clientModel)
+ return;
+
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
}
@@ -660,17 +680,16 @@ void RPCConsole::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
- // peerWidget needs a resize in case the dialog has non-default geometry
- columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
-
- // start PeerTableModel auto refresh
- clientModel->getPeerTableModel()->startAutoRefresh(1000);
+ initPeerTable();
}
void RPCConsole::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
+ if (!clientModel)
+ return;
+
// stop PeerTableModel auto refresh
clientModel->getPeerTableModel()->stopAutoRefresh();
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 3aeff3eace..94672b30cc 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -47,6 +47,8 @@ protected:
private:
/** show detailed information on ui about selected node */
void updateNodeDetail(const CNodeCombinedStats *combinedStats);
+ /** initialize peer table */
+ void initPeerTable();
enum ColumnWidths
{
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index a67f266a13..253693e624 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -276,7 +276,9 @@ Value getblock(const Array& params, bool fHelp)
CBlock block;
CBlockIndex* pblockindex = mapBlockIndex[hash];
- ReadBlockFromDisk(block, pblockindex);
+
+ if(!ReadBlockFromDisk(block, pblockindex))
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
if (!fVerbose)
{
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index c7621dc137..6f72ea7404 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -324,6 +324,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
);
std::string strMode = "template";
+ Value lpval = Value::null;
if (params.size() > 0)
{
const Object& oparam = params[0].get_obj();
@@ -336,6 +337,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
}
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
+ lpval = find_value(oparam, "longpollid");
}
if (strMode != "template")
@@ -347,8 +349,63 @@ Value getblocktemplate(const Array& params, bool fHelp)
if (IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
- // Update block
static unsigned int nTransactionsUpdatedLast;
+
+ if (lpval.type() != null_type)
+ {
+ // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
+ uint256 hashWatchedChain;
+ boost::system_time checktxtime;
+ unsigned int nTransactionsUpdatedLastLP;
+
+ if (lpval.type() == str_type)
+ {
+ // Format: <hashBestChain><nTransactionsUpdatedLast>
+ std::string lpstr = lpval.get_str();
+
+ hashWatchedChain.SetHex(lpstr.substr(0, 64));
+ nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
+ }
+ else
+ {
+ // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
+ hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
+ }
+
+ // Release the wallet and main lock while waiting
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+ LEAVE_CRITICAL_SECTION(cs_main);
+ {
+ checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
+
+ boost::unique_lock<boost::mutex> lock(csBestBlock);
+ while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
+ {
+ if (!cvBlockChange.timed_wait(lock, checktxtime))
+ {
+ // Timeout: Check transactions for update
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
+ break;
+ checktxtime += boost::posix_time::seconds(10);
+ }
+ }
+ }
+ ENTER_CRITICAL_SECTION(cs_main);
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+
+ if (!IsRPCRunning())
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
+ // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
+ }
+
+ // Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
static CBlockTemplate* pblocktemplate;
@@ -436,6 +493,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable));
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index cf2c293caf..680717930b 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -137,6 +137,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("syncheight", statestats.nSyncHeight));
}
obj.push_back(Pair("syncnode", stats.fSyncNode));
+ obj.push_back(Pair("whitelisted", stats.fWhitelisted));
ret.push_back(obj);
}
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 2306b1b883..1efe38e830 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -523,7 +523,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
- " \"redeemScript\": \"hex\" (string, required) redeem script\n"
+ " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
" }\n"
" ,...\n"
" ]\n"
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index e0c96d88fa..5deb6a4e08 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -32,6 +32,7 @@ using namespace std;
static std::string strRPCUserColonPass;
+static bool fRPCRunning = false;
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio::io_service* rpc_io_service = NULL;
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
@@ -232,8 +233,8 @@ static const CRPCCommand vRPCCommands[] =
{ "getblockchaininfo", &getblockchaininfo, true, false, false },
{ "getbestblockhash", &getbestblockhash, true, false, false },
{ "getblockcount", &getblockcount, true, false, false },
- { "getblock", &getblock, false, false, false },
- { "getblockhash", &getblockhash, false, false, false },
+ { "getblock", &getblock, true, false, false },
+ { "getblockhash", &getblockhash, true, false, false },
{ "getdifficulty", &getdifficulty, true, false, false },
{ "getrawmempool", &getrawmempool, true, false, false },
{ "gettxout", &gettxout, true, false, false },
@@ -245,32 +246,32 @@ static const CRPCCommand vRPCCommands[] =
{ "getmininginfo", &getmininginfo, true, false, false },
{ "getnetworkhashps", &getnetworkhashps, true, false, false },
{ "prioritisetransaction", &prioritisetransaction, true, false, false },
- { "submitblock", &submitblock, false, true, false },
+ { "submitblock", &submitblock, true, true, false },
/* Raw transactions */
- { "createrawtransaction", &createrawtransaction, false, false, false },
- { "decoderawtransaction", &decoderawtransaction, false, false, false },
- { "decodescript", &decodescript, false, false, false },
- { "getrawtransaction", &getrawtransaction, false, false, false },
+ { "createrawtransaction", &createrawtransaction, true, false, false },
+ { "decoderawtransaction", &decoderawtransaction, true, false, false },
+ { "decodescript", &decodescript, true, false, false },
+ { "getrawtransaction", &getrawtransaction, true, false, false },
{ "sendrawtransaction", &sendrawtransaction, false, false, false },
{ "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */
/* Utility functions */
{ "createmultisig", &createmultisig, true, true , false },
{ "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */
- { "verifymessage", &verifymessage, false, false, false },
+ { "verifymessage", &verifymessage, true, false, false },
{ "estimatefee", &estimatefee, true, true, false },
{ "estimatepriority", &estimatepriority, true, true, false },
#ifdef ENABLE_WALLET
/* Wallet */
- { "addmultisigaddress", &addmultisigaddress, false, false, true },
+ { "addmultisigaddress", &addmultisigaddress, true, false, true },
{ "backupwallet", &backupwallet, true, false, true },
{ "dumpprivkey", &dumpprivkey, true, false, true },
{ "dumpwallet", &dumpwallet, true, false, true },
- { "encryptwallet", &encryptwallet, false, false, true },
+ { "encryptwallet", &encryptwallet, true, false, true },
{ "getaccountaddress", &getaccountaddress, true, false, true },
- { "getaccount", &getaccount, false, false, true },
+ { "getaccount", &getaccount, true, false, true },
{ "getaddressesbyaccount", &getaddressesbyaccount, true, false, true },
{ "getbalance", &getbalance, false, false, true },
{ "getnewaddress", &getnewaddress, true, false, true },
@@ -279,10 +280,10 @@ static const CRPCCommand vRPCCommands[] =
{ "getreceivedbyaddress", &getreceivedbyaddress, false, false, true },
{ "gettransaction", &gettransaction, false, false, true },
{ "getunconfirmedbalance", &getunconfirmedbalance, false, false, true },
- { "getwalletinfo", &getwalletinfo, true, false, true },
- { "importprivkey", &importprivkey, false, false, true },
- { "importwallet", &importwallet, false, false, true },
- { "importaddress", &importaddress, false, false, true },
+ { "getwalletinfo", &getwalletinfo, false, false, true },
+ { "importprivkey", &importprivkey, true, false, true },
+ { "importwallet", &importwallet, true, false, true },
+ { "importaddress", &importaddress, true, false, true },
{ "keypoolrefill", &keypoolrefill, true, false, true },
{ "listaccounts", &listaccounts, false, false, true },
{ "listaddressgroupings", &listaddressgroupings, false, false, true },
@@ -292,16 +293,16 @@ static const CRPCCommand vRPCCommands[] =
{ "listsinceblock", &listsinceblock, false, false, true },
{ "listtransactions", &listtransactions, false, false, true },
{ "listunspent", &listunspent, false, false, true },
- { "lockunspent", &lockunspent, false, false, true },
+ { "lockunspent", &lockunspent, true, false, true },
{ "move", &movecmd, false, false, true },
{ "sendfrom", &sendfrom, false, false, true },
{ "sendmany", &sendmany, false, false, true },
{ "sendtoaddress", &sendtoaddress, false, false, true },
{ "setaccount", &setaccount, true, false, true },
- { "settxfee", &settxfee, false, false, true },
- { "signmessage", &signmessage, false, false, true },
+ { "settxfee", &settxfee, true, false, true },
+ { "signmessage", &signmessage, true, false, true },
{ "walletlock", &walletlock, true, false, true },
- { "walletpassphrasechange", &walletpassphrasechange, false, false, true },
+ { "walletpassphrasechange", &walletpassphrasechange, true, false, true },
{ "walletpassphrase", &walletpassphrase, true, false, true },
/* Wallet-enabled mining */
@@ -659,6 +660,7 @@ void StartRPCThreads()
rpc_worker_group = new boost::thread_group();
for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
void StartDummyRPCThread()
@@ -671,12 +673,15 @@ void StartDummyRPCThread()
rpc_dummy_work = new asio::io_service::work(*rpc_io_service);
rpc_worker_group = new boost::thread_group();
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
}
void StopRPCThreads()
{
if (rpc_io_service == NULL) return;
+ // Set this to false first, so that longpolling loops will exit when woken up
+ fRPCRunning = false;
// First, cancel all timers and acceptors
// This is not done automatically by ->stop(), and in some cases the destructor of
@@ -698,6 +703,7 @@ void StopRPCThreads()
deadlineTimers.clear();
rpc_io_service->stop();
+ cvBlockChange.notify_all();
if (rpc_worker_group != NULL)
rpc_worker_group->join_all();
delete rpc_dummy_work; rpc_dummy_work = NULL;
@@ -706,6 +712,11 @@ void StopRPCThreads()
delete rpc_io_service; rpc_io_service = NULL;
}
+bool IsRPCRunning()
+{
+ return fRPCRunning;
+}
+
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
{
if (!err)
diff --git a/src/rpcserver.h b/src/rpcserver.h
index e32eb975a1..31badadd6d 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -40,6 +40,8 @@ void StartRPCThreads();
void StartDummyRPCThread();
/* Stop RPC threads */
void StopRPCThreads();
+/* Query whether RPC is running */
+bool IsRPCRunning();
/*
Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 1e46129065..e8c62fd37b 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -201,7 +201,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp)
CReserveKey reservekey(pwalletMain);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey))
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change");
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
reservekey.KeepKey();
diff --git a/src/sync.h b/src/sync.h
index 077ed59b89..cd319e0171 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -84,6 +84,9 @@ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
+/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
+typedef boost::condition_variable CConditionVariable;
+
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();
diff --git a/src/ui_interface.h b/src/ui_interface.h
index e9fcd91d41..a48ba237d2 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -63,8 +63,6 @@ public:
/** Force blocking, modal message box dialog (not just OS notification) */
MODAL = 0x10000000U,
- /** Don't bring GUI to foreground. Use for messages during initialization */
- NOSHOWGUI = 0x20000000U,
/** Predefined combinations for certain default usage cases */
MSG_INFORMATION = ICON_INFORMATION,
diff --git a/src/wallet.cpp b/src/wallet.cpp
index d61f01d096..8d27da9d40 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1076,7 +1076,7 @@ int64_t CWallet::GetWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@@ -1092,7 +1092,7 @@ int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@@ -1107,7 +1107,7 @@ int64_t CWallet::GetImmatureWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
- LOCK(cs_wallet);
+ LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@@ -2011,11 +2011,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey)
if (nIndex != -1)
vchPubKey = keypool.vchPubKey;
else {
- if (pwallet->vchDefaultKey.IsValid()) {
- LogPrintf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!");
- vchPubKey = pwallet->vchDefaultKey;
- } else
- return false;
+ return false;
}
}
assert(vchPubKey.IsValid());