aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/combine_logs.py56
-rwxr-xr-xtest/functional/feature_assumevalid.py2
-rwxr-xr-xtest/functional/feature_block.py27
-rwxr-xr-xtest/functional/feature_config_args.py5
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/feature_segwit.py12
-rwxr-xr-xtest/functional/feature_shutdown.py28
-rwxr-xr-xtest/functional/interface_http.py12
-rwxr-xr-xtest/functional/interface_rpc.py46
-rwxr-xr-xtest/functional/mempool_accept.py26
-rwxr-xr-xtest/functional/mempool_persist.py14
-rwxr-xr-xtest/functional/mining_basic.py23
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py8
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py6
-rwxr-xr-xtest/functional/p2p_invalid_messages.py8
-rwxr-xr-xtest/functional/p2p_segwit.py24
-rwxr-xr-xtest/functional/p2p_timeouts.py30
-rwxr-xr-xtest/functional/rpc_bind.py11
-rwxr-xr-xtest/functional/rpc_psbt.py4
-rwxr-xr-xtest/functional/rpc_rawtransaction.py13
-rwxr-xr-xtest/functional/rpc_scantxoutset.py8
-rwxr-xr-xtest/functional/test_framework/messages.py10
-rwxr-xr-xtest/functional/test_framework/mininode.py14
-rw-r--r--test/functional/test_framework/script.py20
-rwxr-xr-xtest/functional/test_framework/test_framework.py31
-rwxr-xr-xtest/functional/test_framework/test_node.py22
-rw-r--r--test/functional/test_framework/util.py2
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/wallet_address_types.py56
-rwxr-xr-xtest/functional/wallet_balance.py133
-rwxr-xr-xtest/functional/wallet_basic.py13
-rwxr-xr-xtest/functional/wallet_coinbase_category.py59
-rwxr-xr-xtest/functional/wallet_encryption.py6
-rwxr-xr-xtest/functional/wallet_importmulti.py930
-rwxr-xr-xtest/functional/wallet_keypool_topup.py63
-rwxr-xr-xtest/functional/wallet_multiwallet.py3
-rwxr-xr-xtest/lint/check-doc.py8
-rwxr-xr-xtest/lint/commit-script-check.sh18
-rwxr-xr-xtest/lint/lint-format-strings.py5
-rwxr-xr-xtest/lint/lint-includes.sh1
-rwxr-xr-xtest/lint/lint-python-dead-code.sh2
-rwxr-xr-xtest/lint/lint-python.sh3
-rwxr-xr-xtest/lint/lint-rpc-help.sh24
-rwxr-xr-xtest/lint/lint-shebang.sh (renamed from test/lint/lint-python-shebang.sh)9
-rw-r--r--test/sanitizer_suppressions/lsan6
-rw-r--r--test/sanitizer_suppressions/tsan16
-rw-r--r--test/sanitizer_suppressions/ubsan36
-rwxr-xr-xtest/util/bitcoin-util-test.py7
-rwxr-xr-xtest/util/rpcauth-test.py6
49 files changed, 1213 insertions, 661 deletions
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 3230d5cb6b..5bb3b5c094 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -2,7 +2,9 @@
"""Combine logs from multiple bitcoin nodes as well as the test_framework log.
This streams the combined log output to stdout. Use combine_logs.py > outputfile
-to write to an outputfile."""
+to write to an outputfile.
+
+If no argument is provided, the most recent test directory will be used."""
import argparse
from collections import defaultdict, namedtuple
@@ -11,6 +13,13 @@ import itertools
import os
import re
import sys
+import tempfile
+
+# N.B.: don't import any local modules here - this script must remain executable
+# without the parent module installed.
+
+# Should match same symbol in `test_framework.test_framework`.
+TMPDIR_PREFIX = "bitcoin_func_test_"
# Matches on the date format at the start of the log event
TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?Z")
@@ -19,22 +28,30 @@ LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event'])
def main():
"""Main function. Parses args, reads the log files and renders them as text or html."""
-
- parser = argparse.ArgumentParser(usage='%(prog)s [options] <test temporary directory>', description=__doc__)
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument(
+ 'testdir', nargs='?', default='',
+ help=('temporary test directory to combine logs from. '
+ 'Defaults to the most recent'))
parser.add_argument('-c', '--color', dest='color', action='store_true', help='outputs the combined log with events colored by source (requires posix terminal colors. Use less -r for viewing)')
parser.add_argument('--html', dest='html', action='store_true', help='outputs the combined log as html. Requires jinja2. pip install jinja2')
- args, unknown_args = parser.parse_known_args()
+ args = parser.parse_args()
if args.html and args.color:
print("Only one out of --color or --html should be specified")
sys.exit(1)
- # There should only be one unknown argument - the path of the temporary test directory
- if len(unknown_args) != 1:
- print("Unexpected arguments" + str(unknown_args))
+ testdir = args.testdir or find_latest_test_dir()
+
+ if not testdir:
+ print("No test directories found")
sys.exit(1)
- log_events = read_logs(unknown_args[0])
+ if not args.testdir:
+ print("Opening latest test directory: {}".format(testdir), file=sys.stderr)
+
+ log_events = read_logs(testdir)
print_logs(log_events, color=args.color, html=args.html)
@@ -53,6 +70,29 @@ def read_logs(tmp_dir):
return heapq.merge(*[get_log_events(source, f) for source, f in files])
+
+def find_latest_test_dir():
+ """Returns the latest tmpfile test directory prefix."""
+ tmpdir = tempfile.gettempdir()
+
+ def join_tmp(basename):
+ return os.path.join(tmpdir, basename)
+
+ def is_valid_test_tmpdir(basename):
+ fullpath = join_tmp(basename)
+ return (
+ os.path.isdir(fullpath)
+ and basename.startswith(TMPDIR_PREFIX)
+ and os.access(fullpath, os.R_OK)
+ )
+
+ testdir_paths = [
+ join_tmp(name) for name in os.listdir(tmpdir) if is_valid_test_tmpdir(name)
+ ]
+
+ return max(testdir_paths, key=os.path.getmtime) if testdir_paths else None
+
+
def get_log_events(source, logfile):
"""Generator function that returns individual log events.
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 3d0467038d..12a4ce9aff 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -180,7 +180,7 @@ class AssumeValidTest(BitcoinTestFramework):
for i in range(2202):
p2p1.send_message(msg_block(self.blocks[i]))
# Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
- p2p1.sync_with_ping(120)
+ p2p1.sync_with_ping(150)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index e386915ada..b741339351 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -579,14 +579,14 @@ class FullBlockTest(BitcoinTestFramework):
while b47.sha256 < target:
b47.nNonce += 1
b47.rehash()
- self.sync_blocks([b47], False, request_block=False)
+ self.sync_blocks([b47], False, force_send=True, reject_reason='high-hash')
self.log.info("Reject a block with a timestamp >2 hours in the future")
self.move_tip(44)
b48 = self.next_block(48, solve=False)
b48.nTime = int(time.time()) + 60 * 60 * 3
b48.solve()
- self.sync_blocks([b48], False, request_block=False)
+ self.sync_blocks([b48], False, force_send=True, reject_reason='time-too-new')
self.log.info("Reject a block with invalid merkle hash")
self.move_tip(44)
@@ -600,7 +600,7 @@ class FullBlockTest(BitcoinTestFramework):
b50 = self.next_block(50)
b50.nBits = b50.nBits - 1
b50.solve()
- self.sync_blocks([b50], False, request_block=False, reconnect=True)
+ self.sync_blocks([b50], False, force_send=True, reject_reason='bad-diffbits', reconnect=True)
self.log.info("Reject a block with two coinbase transactions")
self.move_tip(44)
@@ -630,7 +630,7 @@ class FullBlockTest(BitcoinTestFramework):
b54 = self.next_block(54, spend=out[15])
b54.nTime = b35.nTime - 1
b54.solve()
- self.sync_blocks([b54], False, request_block=False)
+ self.sync_blocks([b54], False, force_send=True, reject_reason='time-too-old')
# valid timestamp
self.move_tip(53)
@@ -1078,11 +1078,11 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(77)
b80 = self.next_block(80, spend=out[25])
- self.sync_blocks([b80], False, request_block=False)
+ self.sync_blocks([b80], False, force_send=True)
self.save_spendable_output()
b81 = self.next_block(81, spend=out[26])
- self.sync_blocks([b81], False, request_block=False) # other chain is same length
+ self.sync_blocks([b81], False, force_send=True) # other chain is same length
self.save_spendable_output()
b82 = self.next_block(82, spend=out[27])
@@ -1181,7 +1181,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.sync_blocks(blocks, True, timeout=180)
+ self.sync_blocks(blocks, True, timeout=480)
chain1_tip = i
# now create alt chain of same length
@@ -1189,18 +1189,18 @@ class FullBlockTest(BitcoinTestFramework):
blocks2 = []
for i in range(89, LARGE_REORG_SIZE + 89):
blocks2.append(self.next_block("alt" + str(i)))
- self.sync_blocks(blocks2, False, request_block=False)
+ self.sync_blocks(blocks2, False, force_send=True)
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1))
- self.sync_blocks([block], True, timeout=180)
+ self.sync_blocks([block], True, timeout=480)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1)
- self.sync_blocks([block], False, request_block=False)
+ self.sync_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2)
- self.sync_blocks([block], True, timeout=180)
+ self.sync_blocks([block], True, timeout=480)
# Helper methods
################
@@ -1309,14 +1309,15 @@ class FullBlockTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.bootstrap_p2p()
- def sync_blocks(self, blocks, success=True, reject_reason=None, request_block=True, reconnect=False, timeout=60):
+ def sync_blocks(self, blocks, success=True, reject_reason=None, force_send=False, reconnect=False, timeout=60):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, request_block=request_block, timeout=timeout, expect_disconnect=reconnect)
+ self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
if reconnect:
self.reconnect_p2p()
+
if __name__ == '__main__':
FullBlockTest().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 88a9aadc7b..d87eabaa6d 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -34,6 +34,11 @@ class ConfArgsTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
+ conf.write('testnot.datadir=1\n[testnet]\n')
+ self.restart_node(0)
+ self.nodes[0].stop_node(expected_stderr='Warning: Section [testnet] is not recognized.' + os.linesep + 'Warning: Section [testnot] is not recognized.')
+
+ with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
def run_test(self):
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index c162f46d63..11c6712881 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -191,6 +191,8 @@ class PruneTest(BitcoinTestFramework):
def reorg_back(self):
# Verify that a block on the old main chain fork has been pruned away
assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash)
+ with self.nodes[2].assert_debug_log(expected_msgs=['block verification stopping at height', '(pruning, no data)']):
+ self.nodes[2].verifychain(checklevel=4, nblocks=0)
self.log.info("Will need to redownload block %d" % self.forkheight)
# Verify that we have enough history to reorg back to the fork point
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 7098a03f1e..4bcdf9af55 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -90,7 +90,7 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- tmpl = self.nodes[0].getblocktemplate({})
+ tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert(tmpl['sizelimit'] == 1000000)
assert('weightlimit' not in tmpl)
assert(tmpl['sigoplimit'] == 20000)
@@ -232,15 +232,7 @@ class SegWitTest(BitcoinTestFramework):
assert(tx.wit.is_null())
assert(txid3 in self.nodes[0].getrawmempool())
- # Now try calling getblocktemplate() without segwit support.
- template = self.nodes[0].getblocktemplate()
-
- # Check that tx1 is the only transaction of the 3 in the template.
- template_txids = [t['txid'] for t in template['transactions']]
- assert(txid2 not in template_txids and txid3 not in template_txids)
- assert(txid1 in template_txids)
-
- # Check that running with segwit support results in all 3 being included.
+ # Check that getblocktemplate includes all transactions.
template = self.nodes[0].getblocktemplate({"rules": ["segwit"]})
template_txids = [t['txid'] for t in template['transactions']]
assert(txid1 in template_txids)
diff --git a/test/functional/feature_shutdown.py b/test/functional/feature_shutdown.py
new file mode 100755
index 0000000000..b633fabb1f
--- /dev/null
+++ b/test/functional/feature_shutdown.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test bitcoind shutdown."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, get_rpc_proxy
+from threading import Thread
+
+def test_long_call(node):
+ block = node.waitfornewblock()
+ assert_equal(block['height'], 0)
+
+class ShutdownTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ node = get_rpc_proxy(self.nodes[0].url, 1, timeout=600, coveragedir=self.nodes[0].coverage_dir)
+ Thread(target=test_long_call, args=(node,)).start()
+ # wait 1 second to ensure event loop waits for current connections to close
+ self.stop_node(0, wait=1000)
+
+if __name__ == '__main__':
+ ShutdownTest().main()
diff --git a/test/functional/interface_http.py b/test/functional/interface_http.py
index e4b86f9e1e..20889366e5 100755
--- a/test/functional/interface_http.py
+++ b/test/functional/interface_http.py
@@ -31,13 +31,13 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
- assert(conn.sock!=None) #according to http/1.1 connection must still be open!
+ assert(conn.sock is not None) #according to http/1.1 connection must still be open!
#send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1) #must also response with a correct json-rpc message
- assert(conn.sock!=None) #according to http/1.1 connection must still be open!
+ assert(conn.sock is not None) #according to http/1.1 connection must still be open!
conn.close()
#same should be if we add keep-alive because this should be the std. behaviour
@@ -48,13 +48,13 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
- assert(conn.sock!=None) #according to http/1.1 connection must still be open!
+ assert(conn.sock is not None) #according to http/1.1 connection must still be open!
#send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1) #must also response with a correct json-rpc message
- assert(conn.sock!=None) #according to http/1.1 connection must still be open!
+ assert(conn.sock is not None) #according to http/1.1 connection must still be open!
conn.close()
#now do the same with "Connection: close"
@@ -65,7 +65,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
- assert(conn.sock==None) #now the connection must be closed after the response
+ assert(conn.sock is None) #now the connection must be closed after the response
#node1 (2nd node) is running with disabled keep-alive option
urlNode1 = urllib.parse.urlparse(self.nodes[1].url)
@@ -88,7 +88,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
- assert(conn.sock!=None) #connection must be closed because bitcoind should use keep-alive by default
+ assert(conn.sock is not None) #connection must be closed because bitcoind should use keep-alive by default
# Check excessive request size
conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
new file mode 100755
index 0000000000..e3d7b0655d
--- /dev/null
+++ b/test/functional/interface_rpc.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Tests some generic aspects of the RPC interface."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class RPCInterfaceTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def test_batch_request(self):
+ self.log.info("Testing basic JSON-RPC batch request...")
+
+ results = self.nodes[0].batch([
+ # A basic request that will work fine.
+ {"method": "getblockcount", "id": 1},
+ # Request that will fail. The whole batch request should still
+ # work fine.
+ {"method": "invalidmethod", "id": 2},
+ # Another call that should succeed.
+ {"method": "getbestblockhash", "id": 3},
+ ])
+
+ result_by_id = {}
+ for res in results:
+ result_by_id[res["id"]] = res
+
+ assert_equal(result_by_id[1]['error'], None)
+ assert_equal(result_by_id[1]['result'], 0)
+
+ assert_equal(result_by_id[2]['error']['code'], -32601)
+ assert_equal(result_by_id[2]['result'], None)
+
+ assert_equal(result_by_id[3]['error'], None)
+ assert result_by_id[3]['result'] is not None
+
+ def run_test(self):
+ self.test_batch_request()
+
+
+if __name__ == '__main__':
+ RPCInterfaceTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 8847777ba7..02f1cc4391 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -5,6 +5,8 @@
"""Test mempool acceptance of raw transactions."""
from io import BytesIO
+import math
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
@@ -56,6 +58,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.mempool_size = 0
wait_until(lambda: node.getblockcount() == 200)
assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
+ coins = node.listunspent()
self.log.info('Should not accept garbage to testmempoolaccept')
assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
@@ -63,13 +66,14 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))
self.log.info('A transaction already in the blockchain')
- coin = node.listunspent()[0] # Pick a random coin(base) to spend
+ coin = coins.pop() # Pick a random coin(base) to spend
raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': coin['txid'], 'vout': coin['vout']}],
outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}],
))['hex']
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
node.generate(1)
+ self.mempool_size = 0
self.check_mempool_result(
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}],
rawtxs=[raw_tx_in_block],
@@ -89,9 +93,25 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
rawtxs=[raw_tx_0],
)
+ self.log.info('A final transaction not in the mempool')
+ coin = coins.pop() # Pick a random coin(base) to spend
+ raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
+ inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
+ outputs=[{node.getnewaddress(): 0.025}],
+ locktime=node.getblockcount() + 2000, # Can be anything
+ ))['hex']
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ allowhighfees=True,
+ )
+ node.sendrawtransaction(hexstring=raw_tx_final, allowhighfees=True)
+ self.mempool_size += 1
+
self.log.info('A transaction in the mempool')
node.sendrawtransaction(hexstring=raw_tx_0)
- self.mempool_size = 1
+ self.mempool_size += 1
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': '18: txn-already-in-mempool'}],
rawtxs=[raw_tx_0],
@@ -181,7 +201,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('A really large transaction')
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
- tx.vin = [tx.vin[0]] * (MAX_BLOCK_BASE_SIZE // len(tx.vin[0].serialize()))
+ tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize()))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}],
rawtxs=[bytes_to_hex_str(tx.serialize())],
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index b4e9d967fd..d74d4eaaf1 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -42,6 +42,7 @@ import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until
+
class MempoolPersistTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
@@ -60,7 +61,7 @@ class MempoolPersistTest(BitcoinTestFramework):
self.log.debug("Send 5 transactions from node2 (to its own address)")
for i in range(5):
- self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
+ last_txid = self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
node2_balance = self.nodes[2].getbalance()
self.sync_all()
@@ -68,6 +69,13 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getrawmempool()), 5)
assert_equal(len(self.nodes[1].getrawmempool()), 5)
+ self.log.debug("Prioritize a transaction on node0")
+ fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
+ assert_equal(fees['base'], fees['modified'])
+ self.nodes[0].prioritisetransaction(txid=last_txid, fee_delta=1000)
+ fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
+ assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified'])
+
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
# Give this node a head-start, so we can be "extra-sure" that it didn't load anything later
@@ -81,6 +89,10 @@ class MempoolPersistTest(BitcoinTestFramework):
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
assert_equal(len(self.nodes[1].getrawmempool()), 0)
+ self.log.debug('Verify prioritization is loaded correctly')
+ fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
+ assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified'])
+
# Verify accounting of mempool transactions after restart is correct
self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
assert_equal(node2_balance, self.nodes[2].getbalance())
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index 9f01be0646..661d9f4c97 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -25,12 +25,12 @@ from test_framework.util import (
assert_raises_rpc_error,
bytes_to_hex_str as b2x,
)
-
+from test_framework.script import CScriptNum
def assert_template(node, block, expect, rehash=True):
if rehash:
block.hashMerkleRoot = block.calc_merkle_root()
- rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal'})
+ rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal', 'rules': ['segwit']})
assert_equal(rsp, expect)
@@ -60,16 +60,24 @@ class MiningTest(BitcoinTestFramework):
# Mine a block to leave initial block download
node.generatetoaddress(1, node.get_deterministic_priv_key().address)
- tmpl = node.getblocktemplate()
+ tmpl = node.getblocktemplate({'rules': ['segwit']})
self.log.info("getblocktemplate: Test capability advertised")
assert 'proposal' in tmpl['capabilities']
assert 'coinbasetxn' not in tmpl
- coinbase_tx = create_coinbase(height=int(tmpl["height"]) + 1)
+ next_height = int(tmpl["height"])
+ coinbase_tx = create_coinbase(height=next_height)
# sequence numbers must not be max for nLockTime to have effect
coinbase_tx.vin[0].nSequence = 2 ** 32 - 2
coinbase_tx.rehash()
+ # round-trip the encoded bip34 block height commitment
+ assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height)
+ # round-trip negative and multi-byte CScriptNums to catch python regression
+ assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500)
+ assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500)
+ assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1)
+
block = CBlock()
block.nVersion = tmpl["version"]
block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
@@ -78,6 +86,9 @@ class MiningTest(BitcoinTestFramework):
block.nNonce = 0
block.vtx = [coinbase_tx]
+ self.log.info("getblocktemplate: segwit rule must be set")
+ assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate)
+
self.log.info("getblocktemplate: Test valid block")
assert_template(node, block, None)
@@ -94,7 +105,7 @@ class MiningTest(BitcoinTestFramework):
assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize()))
self.log.info("getblocktemplate: Test truncated final transaction")
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit']})
self.log.info("getblocktemplate: Test duplicate transaction")
bad_block = copy.deepcopy(block)
@@ -124,7 +135,7 @@ class MiningTest(BitcoinTestFramework):
bad_block_sn = bytearray(block.serialize())
assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1)
bad_block_sn[TX_COUNT_OFFSET] += 1
- assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'})
+ assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit']})
self.log.info("getblocktemplate: Test bad bits")
bad_block = copy.deepcopy(block)
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index 9a3c15a4a7..72cde8e811 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -15,14 +15,14 @@ class LongpollThread(threading.Thread):
def __init__(self, node):
threading.Thread.__init__(self)
# query current longpollid
- template = node.getblocktemplate()
+ template = node.getblocktemplate({'rules': ['segwit']})
self.longpollid = template['longpollid']
# create a new connection to the node, we can't use the same
# connection from two threads
self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir)
def run(self):
- self.node.getblocktemplate({'longpollid':self.longpollid})
+ self.node.getblocktemplate({'longpollid': self.longpollid, 'rules': ['segwit']})
class GetBlockTemplateLPTest(BitcoinTestFramework):
def set_test_params(self):
@@ -34,10 +34,10 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.")
self.nodes[0].generate(10)
- template = self.nodes[0].getblocktemplate()
+ template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
longpollid = template['longpollid']
# longpollid should not change between successive invocations if nothing else happens
- template2 = self.nodes[0].getblocktemplate()
+ template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert(template2['longpollid'] == longpollid)
# Test 1: test that the longpolling wait if we do nothing
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index c5ddee56f1..ca4b621a78 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -84,7 +84,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
high_fee_tx = x
# Something high-fee should have been mined!
- assert(high_fee_tx != None)
+ assert(high_fee_tx is not None)
# Add a prioritisation before a tx is in the mempool (de-prioritising a
# high-fee transaction so that it's now low fee).
@@ -142,10 +142,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# getblocktemplate to (eventually) return a new block.
mock_time = int(time.time())
self.nodes[0].setmocktime(mock_time)
- template = self.nodes[0].getblocktemplate()
+ template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN))
self.nodes[0].setmocktime(mock_time+10)
- new_template = self.nodes[0].getblocktemplate()
+ new_template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert(template != new_template)
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index a2d40fab1a..dbc5c5fff6 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -3,6 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test node responses to invalid network messages."""
+import os
import struct
from test_framework import messages
@@ -66,7 +67,10 @@ class InvalidMessagesTest(BitcoinTestFramework):
msg_at_size = msg_unrecognized("b" * valid_data_limit)
assert len(msg_at_size.serialize()) == msg_limit
- with node.assert_memory_usage_stable(perc_increase_allowed=0.03):
+ increase_allowed = 0.5
+ if [s for s in os.environ.get("BITCOIN_CONFIG", "").split(" ") if "--with-sanitizers" in s and "address" in s]:
+ increase_allowed = 3.5
+ with node.assert_memory_usage_stable(increase_allowed=increase_allowed):
self.log.info(
"Sending a bunch of large, junk messages to test "
"memory exhaustion. May take a bit...")
@@ -82,7 +86,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
# Peer 1, despite serving up a bunch of nonsense, should still be connected.
self.log.info("Waiting for node to drop junk messages.")
- node.p2p.sync_with_ping(timeout=30)
+ node.p2p.sync_with_ping(timeout=120)
assert node.p2p.is_connected
#
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index afbbfa8992..d95da227e5 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -545,31 +545,13 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def test_getblocktemplate_before_lockin(self):
- # Node0 is segwit aware, node2 is not.
- for node in [self.nodes[0], self.nodes[2]]:
- gbt_results = node.getblocktemplate()
- block_version = gbt_results['version']
- # If we're not indicating segwit support, we will still be
- # signalling for segwit activation.
- assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0])
- # If we don't specify the segwit rule, then we won't get a default
- # commitment.
- assert('default_witness_commitment' not in gbt_results)
-
- # Workaround:
- # Can either change the tip, or change the mempool and wait 5 seconds
- # to trigger a recomputation of getblocktemplate.
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
- # Using mocktime lets us avoid sleep()
- sync_mempools(self.nodes)
- self.nodes[0].setmocktime(int(time.time()) + 10)
- self.nodes[2].setmocktime(int(time.time()) + 10)
for node in [self.nodes[0], self.nodes[2]]:
gbt_results = node.getblocktemplate({"rules": ["segwit"]})
block_version = gbt_results['version']
if node == self.nodes[2]:
- # If this is a non-segwit node, we should still not get a witness
+ # If this is a non-segwit node, we should not get a witness
# commitment, nor a version bit signalling segwit.
assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
assert('default_witness_commitment' not in gbt_results)
@@ -586,10 +568,6 @@ class SegWitTest(BitcoinTestFramework):
script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, bytes_to_hex_str(script))
- # undo mocktime
- self.nodes[0].setmocktime(0)
- self.nodes[2].setmocktime(0)
-
@subtest
def advance_to_segwit_lockin(self):
"""Mine enough blocks to lock in segwit, but don't activate."""
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index 2459a9f243..02ceec3dc1 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -14,11 +14,11 @@
- Wait 1 second
- Assert that we're connected
- Send a ping to no_verack_node and no_version_node
-- Wait 30 seconds
+- Wait 1 second
- Assert that we're still connected
- Send a ping to no_verack_node and no_version_node
-- Wait 31 seconds
-- Assert that we're no longer connected (timeout to receive version/verack is 60 seconds)
+- Wait 2 seconds
+- Assert that we're no longer connected (timeout to receive version/verack is 3 seconds)
"""
from time import sleep
@@ -36,6 +36,8 @@ class TimeoutsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ # set timeout to receive version/verack to 3 seconds
+ self.extra_args = [["-peertimeout=3"]]
def run_test(self):
# Setup the p2p connections
@@ -52,7 +54,7 @@ class TimeoutsTest(BitcoinTestFramework):
no_verack_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
- sleep(30)
+ sleep(1)
assert "version" in no_verack_node.last_message
@@ -63,11 +65,21 @@ class TimeoutsTest(BitcoinTestFramework):
no_verack_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
- sleep(31)
-
- assert not no_verack_node.is_connected
- assert not no_version_node.is_connected
- assert not no_send_node.is_connected
+ expected_timeout_logs = [
+ "version handshake timeout from 0",
+ "socket no message in first 3 seconds, 1 0 from 1",
+ "socket no message in first 3 seconds, 0 0 from 2",
+ ]
+
+ with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs):
+ sleep(3)
+ # By now, we waited a total of 5 seconds. Off-by-two for two
+ # reasons:
+ # * The internal precision is one second
+ # * Account for network delay
+ assert not no_verack_node.is_connected
+ assert not no_version_node.is_connected
+ assert not no_send_node.is_connected
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index 53916d5290..c23aa13685 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -48,9 +48,12 @@ class RPCBindTest(BitcoinTestFramework):
at a non-localhost IP.
'''
self.log.info("Allow IP test for %s:%d" % (rpchost, rpcport))
- base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips]
+ node_args = \
+ ['-disablewallet', '-nolisten'] + \
+ ['-rpcallowip='+x for x in allow_ips] + \
+ ['-rpcbind='+addr for addr in ['127.0.0.1', "%s:%d" % (rpchost, rpcport)]] # Bind to localhost as well so start_nodes doesn't hang
self.nodes[0].rpchost = None
- self.start_nodes([base_args])
+ self.start_nodes([node_args])
# connect to node through non-loopback interface
node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
node.getnetworkinfo()
@@ -101,9 +104,9 @@ class RPCBindTest(BitcoinTestFramework):
# check default without rpcallowip (IPv4 and IPv6 localhost)
self.run_bind_test(None, '127.0.0.1', [],
[('127.0.0.1', self.defaultport), ('::1', self.defaultport)])
- # check default with rpcallowip (IPv6 any)
+ # check default with rpcallowip (IPv4 and IPv6 localhost)
self.run_bind_test(['127.0.0.1'], '127.0.0.1', [],
- [('::0', self.defaultport)])
+ [('127.0.0.1', self.defaultport), ('::1', self.defaultport)])
# check only IPv6 localhost (explicit)
self.run_bind_test(['[::1]'], '[::1]', ['[::1]'],
[('::1', self.defaultport)])
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 04d9bb65a6..272ebe65cb 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -210,6 +210,10 @@ class PSBTTest(BitcoinTestFramework):
assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
assert_equal(decoded_psbt["tx"]["locktime"], 0)
+ # Make sure change address wallet does not have P2SH innerscript access to results in success
+ # when attempting BnB coin selection
+ self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
+
# Regression test for 14473 (mishandling of already-signed witness transaction):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"])
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 8ed490f552..fc012e6e3a 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -100,6 +100,8 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1})
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)]))
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}])
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{"data": 'aa'}, {"data": "bb"}])
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", 'aa'), ("data", "bb")]))
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}])
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']])
@@ -127,19 +129,12 @@ class RawTransactionsTest(BitcoinTestFramework):
bytes_to_hex_str(tx.serialize()),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]),
)
- # Two data outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([('data', '99'), ('data', '99')])))))
- assert_equal(len(tx.vout), 2)
- assert_equal(
- bytes_to_hex_str(tx.serialize()),
- self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{'data': '99'}, {'data': '99'}]),
- )
# Multiple mixed outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), ('data', '99'), ('data', '99')])))))
+ tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')])))))
assert_equal(len(tx.vout), 3)
assert_equal(
bytes_to_hex_str(tx.serialize()),
- self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {'data': '99'}, {'data': '99'}]),
+ self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}, {'data': '99'}]),
)
for type in ["bech32", "p2sh-segwit", "legacy"]:
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index 881b839a4e..11b4db6ec5 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -10,6 +10,9 @@ from decimal import Decimal
import shutil
import os
+def descriptors(out):
+ return sorted(u['desc'] for u in out['unspents'])
+
class ScantxoutsetTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -93,5 +96,10 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1499}])['total_amount'], Decimal("12.288"))
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])['total_amount'], Decimal("28.672"))
+ # Test the reported descriptors for a few matches
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", "range": 1499}])), ["pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)", "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)"])
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)"])
+ assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])), ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)', 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)', 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)'])
+
if __name__ == '__main__':
ScantxoutsetTest().main()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index c72cb8835c..356a45d6d0 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -450,6 +450,8 @@ class CTransaction:
if flags != 0:
self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))]
self.wit.deserialize(f)
+ else:
+ self.wit = CTxWitness()
self.nLockTime = struct.unpack("<I", f.read(4))[0]
self.sha256 = None
self.hash = None
@@ -764,7 +766,7 @@ class HeaderAndShortIDs:
self.prefilled_txn = []
self.use_witness = False
- if p2pheaders_and_shortids != None:
+ if p2pheaders_and_shortids is not None:
self.header = p2pheaders_and_shortids.header
self.nonce = p2pheaders_and_shortids.nonce
self.shortids = p2pheaders_and_shortids.shortids
@@ -822,7 +824,7 @@ class BlockTransactionsRequest:
def __init__(self, blockhash=0, indexes = None):
self.blockhash = blockhash
- self.indexes = indexes if indexes != None else []
+ self.indexes = indexes if indexes is not None else []
def deserialize(self, f):
self.blockhash = deser_uint256(f)
@@ -863,7 +865,7 @@ class BlockTransactions:
def __init__(self, blockhash=0, transactions = None):
self.blockhash = blockhash
- self.transactions = transactions if transactions != None else []
+ self.transactions = transactions if transactions is not None else []
def deserialize(self, f):
self.blockhash = deser_uint256(f)
@@ -1052,7 +1054,7 @@ class msg_getdata:
command = b"getdata"
def __init__(self, inv=None):
- self.inv = inv if inv != None else []
+ self.inv = inv if inv is not None else []
def deserialize(self, f):
self.inv = deser_vector(f, CInv)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 388c123055..ca5734d67d 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -511,14 +511,14 @@ class P2PDataStore(P2PInterface):
if response is not None:
self.send_message(response)
- def send_blocks_and_test(self, blocks, node, *, success=True, request_block=True, reject_reason=None, expect_disconnect=False, timeout=60):
+ def send_blocks_and_test(self, blocks, node, *, success=True, force_send=False, reject_reason=None, expect_disconnect=False, timeout=60):
"""Send blocks to test node and test whether the tip advances.
- add all blocks to our block_store
- send a headers message for the final block
- the on_getheaders handler will ensure that any getheaders are responded to
- - if request_block is True: wait for getdata for each of the blocks. The on_getdata handler will
- ensure that any getdata messages are responded to
+ - if force_send is False: wait for getdata for each of the blocks. The on_getdata handler will
+ ensure that any getdata messages are responded to. Otherwise send the full block unsolicited.
- if success is True: assert that the node's tip advances to the most recent block
- if success is False: assert that the node's tip doesn't advance
- if reject_reason is set: assert that the correct reject message is logged"""
@@ -530,9 +530,11 @@ class P2PDataStore(P2PInterface):
reject_reason = [reject_reason] if reject_reason else []
with node.assert_debug_log(expected_msgs=reject_reason):
- self.send_message(msg_headers([CBlockHeader(blocks[-1])]))
-
- if request_block:
+ if force_send:
+ for b in blocks:
+ self.send_message(msg_block(block=b))
+ else:
+ self.send_message(msg_headers([CBlockHeader(blocks[-1])]))
wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock)
if expect_disconnect:
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 2fe44010ba..012c80a1be 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -385,6 +385,22 @@ class CScriptNum:
r[-1] |= 0x80
return bytes([len(r)]) + r
+ @staticmethod
+ def decode(vch):
+ result = 0
+ # We assume valid push_size and minimal encoding
+ value = vch[1:]
+ if len(value) == 0:
+ return result
+ for i, byte in enumerate(value):
+ result |= int(byte) << 8*i
+ if value[-1] >= 0x80:
+ # Mask for all but the highest result bit
+ num_mask = (2**(len(value)*8) - 1) >> 1
+ result &= num_mask
+ result *= -1
+ return result
+
class CScript(bytes):
"""Serialized script
@@ -434,6 +450,10 @@ class CScript(bytes):
# join makes no sense for a CScript()
raise NotImplementedError
+ # Python 3.4 compatibility
+ def hex(self):
+ return hexlify(self).decode('ascii')
+
def __new__(cls, value=b''):
if isinstance(value, bytes) or isinstance(value, bytearray):
return super(CScript, cls).__new__(cls, value)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 705b5daa5c..8c87f529e4 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -43,6 +43,8 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
TEST_EXIT_SKIPPED = 77
+TMPDIR_PREFIX = "bitcoin_func_test_"
+
class SkipTest(Exception):
"""This exception is raised to skip a test"""
@@ -151,7 +153,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.tmpdir = os.path.abspath(self.options.tmpdir)
os.makedirs(self.options.tmpdir, exist_ok=False)
else:
- self.options.tmpdir = tempfile.mkdtemp(prefix="test")
+ self.options.tmpdir = tempfile.mkdtemp(prefix=TMPDIR_PREFIX)
self._start_logging()
self.log.debug('Setting up network thread')
@@ -279,7 +281,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Public helper methods. These can be accessed by the subclass test scripts.
def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None):
- """Instantiate TestNode objects"""
+ """Instantiate TestNode objects.
+
+ Should only be called once after the nodes have been specified in
+ set_test_params()."""
if self.bind_to_localhost_only:
extra_confs = [["bind=127.0.0.1"]] * num_nodes
else:
@@ -292,7 +297,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert_equal(len(extra_args), num_nodes)
assert_equal(len(binary), num_nodes)
for i in range(num_nodes):
- self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=self.rpc_timewait, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli))
+ self.nodes.append(TestNode(
+ i,
+ get_datadir_path(self.options.tmpdir, i),
+ rpchost=rpchost,
+ timewait=self.rpc_timewait,
+ bitcoind=binary[i],
+ bitcoin_cli=self.options.bitcoincli,
+ mocktime=self.mocktime,
+ coverage_dir=self.options.coveragedir,
+ extra_conf=extra_confs[i],
+ extra_args=extra_args[i],
+ use_cli=self.options.usecli,
+ ))
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""
@@ -325,16 +342,16 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
for node in self.nodes:
coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc)
- def stop_node(self, i, expected_stderr=''):
+ def stop_node(self, i, expected_stderr='', wait=0):
"""Stop a bitcoind test node"""
- self.nodes[i].stop_node(expected_stderr)
+ self.nodes[i].stop_node(expected_stderr, wait=wait)
self.nodes[i].wait_until_stopped()
- def stop_nodes(self):
+ def stop_nodes(self, wait=0):
"""Stop multiple bitcoind test nodes"""
for node in self.nodes:
# Issue RPC to stop nodes
- node.stop_node()
+ node.stop_node(wait=wait)
for node in self.nodes:
# Wait for nodes to stop
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 9dcc0e6d0e..031a8824b1 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -68,7 +68,7 @@ class TestNode():
self.rpc_timeout = timewait
self.binary = bitcoind
self.coverage_dir = coverage_dir
- if extra_conf != None:
+ if extra_conf is not None:
append_config(datadir, extra_conf)
# Most callers will just need to add extra args to the standard list below.
# For those callers that need more flexibility, they can just set the args property directly.
@@ -115,7 +115,7 @@ class TestNode():
]
return PRIV_KEYS[self.index]
- def get_mem_rss(self):
+ def get_mem_rss_kilobytes(self):
"""Get the memory usage (RSS) per `ps`.
Returns None if `ps` is unavailable.
@@ -228,13 +228,13 @@ class TestNode():
wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name))
return self.rpc / wallet_path
- def stop_node(self, expected_stderr=''):
+ def stop_node(self, expected_stderr='', wait=0):
"""Stop the node."""
if not self.running:
return
self.log.debug("Stopping node")
try:
- self.stop()
+ self.stop(wait=wait)
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
@@ -291,15 +291,19 @@ class TestNode():
self._raise_assertion_error('Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log))
@contextlib.contextmanager
- def assert_memory_usage_stable(self, perc_increase_allowed=0.03):
+ def assert_memory_usage_stable(self, *, increase_allowed=0.03):
"""Context manager that allows the user to assert that a node's memory usage (RSS)
hasn't increased beyond some threshold percentage.
+
+ Args:
+ increase_allowed (float): the fractional increase in memory allowed until failure;
+ e.g. `0.12` for up to 12% increase allowed.
"""
- before_memory_usage = self.get_mem_rss()
+ before_memory_usage = self.get_mem_rss_kilobytes()
yield
- after_memory_usage = self.get_mem_rss()
+ after_memory_usage = self.get_mem_rss_kilobytes()
if not (before_memory_usage and after_memory_usage):
self.log.warning("Unable to detect memory usage (RSS) - skipping memory check.")
@@ -307,10 +311,10 @@ class TestNode():
perc_increase_memory_usage = (after_memory_usage / before_memory_usage) - 1
- if perc_increase_memory_usage > perc_increase_allowed:
+ if perc_increase_memory_usage > increase_allowed:
self._raise_assertion_error(
"Memory usage increased over threshold of {:.3f}% from {} to {} ({:.3f}%)".format(
- perc_increase_allowed * 100, before_memory_usage, after_memory_usage,
+ increase_allowed * 100, before_memory_usage, after_memory_usage,
perc_increase_memory_usage * 100))
def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs):
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index b355816d8b..d0a78d8dfd 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -326,7 +326,7 @@ def get_auth_cookie(datadir):
if line.startswith("rpcpassword="):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
- if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
+ if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")) and os.access(os.path.join(datadir, "regtest", ".cookie"), os.R_OK):
with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 5541b44690..a094433942 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -120,6 +120,7 @@ BASE_SCRIPTS = [
'wallet_disableprivatekeys.py',
'wallet_disableprivatekeys.py --usecli',
'interface_http.py',
+ 'interface_rpc.py',
'rpc_psbt.py',
'rpc_users.py',
'feature_proxy.py',
@@ -152,6 +153,7 @@ BASE_SCRIPTS = [
'wallet_importprunedfunds.py',
'p2p_leak_tx.py',
'rpc_signmessage.py',
+ 'wallet_balance.py',
'feature_nulldummy.py',
'mempool_accept.py',
'wallet_import_rescan.py',
@@ -174,6 +176,7 @@ BASE_SCRIPTS = [
'rpc_getblockstats.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
+ 'wallet_coinbase_category.py',
'feature_filelock.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
@@ -184,6 +187,7 @@ BASE_SCRIPTS = [
'feature_config_args.py',
'rpc_help.py',
'feature_help.py',
+ 'feature_shutdown.py',
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]
@@ -271,7 +275,7 @@ def main():
if tests:
# Individual tests have been specified. Run specified tests that exist
# in the ALL_SCRIPTS list. Accept the name with or without .py extension.
- tests = [re.sub("\.py$", "", test) + ".py" for test in tests]
+ tests = [test + ".py" if ".py" not in test else test for test in tests]
for test in tests:
if test in ALL_SCRIPTS:
test_list.append(test)
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index 0f75045c9d..bafa556aad 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -99,6 +99,8 @@ class AddressTypeTest(BitcoinTestFramework):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
assert(self.nodes[node].validateaddress(address)['isvalid'])
+ assert_equal(info.get('solvable'), True)
+
if not multisig and typ == 'legacy':
# P2PKH
assert(not info['isscript'])
@@ -146,6 +148,47 @@ class AddressTypeTest(BitcoinTestFramework):
# Unknown type
assert(False)
+ def test_desc(self, node, address, multisig, typ, utxo):
+ """Run sanity checks on a descriptor reported by getaddressinfo."""
+ info = self.nodes[node].getaddressinfo(address)
+ assert('desc' in info)
+ assert_equal(info['desc'], utxo['desc'])
+ assert(self.nodes[node].validateaddress(address)['isvalid'])
+
+ # Use a ridiculously roundabout way to find the key origin info through
+ # the PSBT logic. However, this does test consistency between the PSBT reported
+ # fingerprints/paths and the descriptor logic.
+ psbt = self.nodes[node].createpsbt([{'txid':utxo['txid'], 'vout':utxo['vout']}],[{address:0.00010000}])
+ psbt = self.nodes[node].walletprocesspsbt(psbt, False, "ALL", True)
+ decode = self.nodes[node].decodepsbt(psbt['psbt'])
+ key_descs = {}
+ for deriv in decode['inputs'][0]['bip32_derivs']:
+ assert_equal(len(deriv['master_fingerprint']), 8)
+ assert_equal(deriv['path'][0], 'm')
+ key_descs[deriv['pubkey']] = '[' + deriv['master_fingerprint'] + deriv['path'][1:] + ']' + deriv['pubkey']
+
+ if not multisig and typ == 'legacy':
+ # P2PKH
+ assert_equal(info['desc'], "pkh(%s)" % key_descs[info['pubkey']])
+ elif not multisig and typ == 'p2sh-segwit':
+ # P2SH-P2WPKH
+ assert_equal(info['desc'], "sh(wpkh(%s))" % key_descs[info['pubkey']])
+ elif not multisig and typ == 'bech32':
+ # P2WPKH
+ assert_equal(info['desc'], "wpkh(%s)" % key_descs[info['pubkey']])
+ elif typ == 'legacy':
+ # P2SH-multisig
+ assert_equal(info['desc'], "sh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]]))
+ elif typ == 'p2sh-segwit':
+ # P2SH-P2WSH-multisig
+ assert_equal(info['desc'], "sh(wsh(multi(2,%s,%s)))" % (key_descs[info['embedded']['pubkeys'][0]], key_descs[info['embedded']['pubkeys'][1]]))
+ elif typ == 'bech32':
+ # P2WSH-multisig
+ assert_equal(info['desc'], "wsh(multi(2,%s,%s))" % (key_descs[info['pubkeys'][0]], key_descs[info['pubkeys'][1]]))
+ else:
+ # Unknown type
+ assert(False)
+
def test_change_output_type(self, node_sender, destinations, expected_type):
txid = self.nodes[node_sender].sendmany(dummy="", amounts=dict.fromkeys(destinations, 0.001))
raw_tx = self.nodes[node_sender].getrawtransaction(txid)
@@ -198,6 +241,7 @@ class AddressTypeTest(BitcoinTestFramework):
self.log.debug("Old balances are {}".format(old_balances))
to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
sends = {}
+ addresses = {}
self.log.debug("Prepare sends")
for n, to_node in enumerate(range(from_node, from_node + 4)):
@@ -228,6 +272,7 @@ class AddressTypeTest(BitcoinTestFramework):
# Output entry
sends[address] = to_send * 10 * (1 + n)
+ addresses[to_node] = (address, typ)
self.log.debug("Sending: {}".format(sends))
self.nodes[from_node].sendmany("", sends)
@@ -244,6 +289,17 @@ class AddressTypeTest(BitcoinTestFramework):
self.nodes[5].generate(1)
sync_blocks(self.nodes)
+ # Verify that the receiving wallet contains a UTXO with the expected address, and expected descriptor
+ for n, to_node in enumerate(range(from_node, from_node + 4)):
+ to_node %= 4
+ found = False
+ for utxo in self.nodes[to_node].listunspent():
+ if utxo['address'] == addresses[to_node][0]:
+ found = True
+ self.test_desc(to_node, addresses[to_node][0], multisig, addresses[to_node][1], utxo)
+ break
+ assert found
+
new_balances = self.get_balances()
self.log.debug("Check new balances: {}".format(new_balances))
# We don't know what fee was set, so we can only check bounds on the balance of the sending node
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
new file mode 100755
index 0000000000..05c97e0340
--- /dev/null
+++ b/test/functional/wallet_balance.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the wallet balance RPC methods."""
+from decimal import Decimal
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+RANDOM_COINBASE_ADDRESS = 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ'
+
+def create_transactions(node, address, amt, fees):
+ # Create and sign raw transactions from node to address for amt.
+ # Creates a transaction for each fee and returns an array
+ # of the raw transactions.
+ utxos = node.listunspent(0)
+
+ # Create transactions
+ inputs = []
+ ins_total = 0
+ for utxo in utxos:
+ inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
+ ins_total += utxo['amount']
+ if ins_total > amt:
+ break
+
+ txs = []
+ for fee in fees:
+ outputs = {address: amt, node.getrawchangeaddress(): ins_total - amt - fee}
+ raw_tx = node.createrawtransaction(inputs, outputs, 0, True)
+ raw_tx = node.signrawtransactionwithwallet(raw_tx)
+ txs.append(raw_tx)
+
+ return txs
+
+class WalletTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Check that nodes don't own any UTXOs
+ assert_equal(len(self.nodes[0].listunspent()), 0)
+ assert_equal(len(self.nodes[1].listunspent()), 0)
+
+ self.log.info("Mining one block for each node")
+
+ self.nodes[0].generate(1)
+ self.sync_all()
+ self.nodes[1].generate(1)
+ self.nodes[1].generatetoaddress(100, RANDOM_COINBASE_ADDRESS)
+ self.sync_all()
+
+ assert_equal(self.nodes[0].getbalance(), 50)
+ assert_equal(self.nodes[1].getbalance(), 50)
+
+ self.log.info("Test getbalance with different arguments")
+ assert_equal(self.nodes[0].getbalance("*"), 50)
+ assert_equal(self.nodes[0].getbalance("*", 1), 50)
+ assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
+ assert_equal(self.nodes[0].getbalance(minconf=1), 50)
+
+ # Send 40 BTC from 0 to 1 and 60 BTC from 1 to 0.
+ txs = create_transactions(self.nodes[0], self.nodes[1].getnewaddress(), 40, [Decimal('0.01')])
+ self.nodes[0].sendrawtransaction(txs[0]['hex'])
+ self.nodes[1].sendrawtransaction(txs[0]['hex']) # sending on both nodes is faster than waiting for propagation
+
+ self.sync_all()
+ txs = create_transactions(self.nodes[1], self.nodes[0].getnewaddress(), 60, [Decimal('0.01'), Decimal('0.02')])
+ self.nodes[1].sendrawtransaction(txs[0]['hex'])
+ self.nodes[0].sendrawtransaction(txs[0]['hex']) # sending on both nodes is faster than waiting for propagation
+ self.sync_all()
+
+ # First argument of getbalance must be set to "*"
+ assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"", self.nodes[1].getbalance, "")
+
+ self.log.info("Test getbalance and getunconfirmedbalance with unconfirmed inputs")
+
+ # getbalance without any arguments includes unconfirmed transactions, but not untrusted transactions
+ assert_equal(self.nodes[0].getbalance(), Decimal('9.99')) # change from node 0's send
+ assert_equal(self.nodes[1].getbalance(), Decimal('29.99')) # change from node 1's send
+ # Same with minconf=0
+ assert_equal(self.nodes[0].getbalance(minconf=0), Decimal('9.99'))
+ assert_equal(self.nodes[1].getbalance(minconf=0), Decimal('29.99'))
+ # getbalance with a minconf incorrectly excludes coins that have been spent more recently than the minconf blocks ago
+ # TODO: fix getbalance tracking of coin spentness depth
+ assert_equal(self.nodes[0].getbalance(minconf=1), Decimal('0'))
+ assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0'))
+ # getunconfirmedbalance
+ assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60')) # output of node 1's spend
+ assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent
+
+ # Node 1 bumps the transaction fee and resends
+ self.nodes[1].sendrawtransaction(txs[1]['hex'])
+ self.sync_all()
+
+ self.log.info("Test getbalance and getunconfirmedbalance with conflicted unconfirmed inputs")
+
+ assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('60')) # output of node 1's send
+ assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('60'))
+ assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0')) # Doesn't include output of node 0's send since it was spent
+ assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0'))
+
+ self.nodes[1].generatetoaddress(1, RANDOM_COINBASE_ADDRESS)
+ self.sync_all()
+
+ # balances are correct after the transactions are confirmed
+ assert_equal(self.nodes[0].getbalance(), Decimal('69.99')) # node 1's send plus change from node 0's send
+ assert_equal(self.nodes[1].getbalance(), Decimal('29.98')) # change from node 0's send
+
+ # Send total balance away from node 1
+ txs = create_transactions(self.nodes[1], self.nodes[0].getnewaddress(), Decimal('29.97'), [Decimal('0.01')])
+ self.nodes[1].sendrawtransaction(txs[0]['hex'])
+ self.nodes[1].generatetoaddress(2, RANDOM_COINBASE_ADDRESS)
+ self.sync_all()
+
+ # getbalance with a minconf incorrectly excludes coins that have been spent more recently than the minconf blocks ago
+ # TODO: fix getbalance tracking of coin spentness depth
+ # getbalance with minconf=3 should still show the old balance
+ assert_equal(self.nodes[1].getbalance(minconf=3), Decimal('0'))
+
+ # getbalance with minconf=2 will show the new balance.
+ assert_equal(self.nodes[1].getbalance(minconf=2), Decimal('0'))
+
+if __name__ == '__main__':
+ WalletTest().main()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index c9b40905f0..7184bb8cb6 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -67,15 +67,6 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 50)
assert_equal(self.nodes[2].getbalance(), 0)
- # Check getbalance with different arguments
- assert_equal(self.nodes[0].getbalance("*"), 50)
- assert_equal(self.nodes[0].getbalance("*", 1), 50)
- assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
- assert_equal(self.nodes[0].getbalance(minconf=1), 50)
-
- # first argument of getbalance must be excluded or set to "*"
- assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"", self.nodes[0].getbalance, "")
-
# Check that only first and second nodes have UTXOs
utxos = self.nodes[0].listunspent()
assert_equal(len(utxos), 1)
@@ -248,10 +239,6 @@ class WalletTest(BitcoinTestFramework):
assert(txid1 in self.nodes[3].getrawmempool())
- # Exercise balance rpcs
- assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1)
- assert_equal(self.nodes[0].getunconfirmedbalance(), 1)
-
# check if we can list zero value tx as available coins
# 1. create raw_tx
# 2. hex-changed one output to 0.0
diff --git a/test/functional/wallet_coinbase_category.py b/test/functional/wallet_coinbase_category.py
new file mode 100755
index 0000000000..7aa8b44ebd
--- /dev/null
+++ b/test/functional/wallet_coinbase_category.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test coinbase transactions return the correct categories.
+
+Tests listtransactions, listsinceblock, and gettransaction.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_array_result
+)
+
+class CoinbaseCategoryTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def assert_category(self, category, address, txid, skip):
+ assert_array_result(self.nodes[0].listtransactions(skip=skip),
+ {"address": address},
+ {"category": category})
+ assert_array_result(self.nodes[0].listsinceblock()["transactions"],
+ {"address": address},
+ {"category": category})
+ assert_array_result(self.nodes[0].gettransaction(txid)["details"],
+ {"address": address},
+ {"category": category})
+
+ def run_test(self):
+ # Generate one block to an address
+ address = self.nodes[0].getnewaddress()
+ self.nodes[0].generatetoaddress(1, address)
+ hash = self.nodes[0].getbestblockhash()
+ txid = self.nodes[0].getblock(hash)["tx"][0]
+
+ # Coinbase transaction is immature after 1 confirmation
+ self.assert_category("immature", address, txid, 0)
+
+ # Mine another 99 blocks on top
+ self.nodes[0].generate(99)
+ # Coinbase transaction is still immature after 100 confirmations
+ self.assert_category("immature", address, txid, 99)
+
+ # Mine one more block
+ self.nodes[0].generate(1)
+ # Coinbase transaction is now matured, so category is "generate"
+ self.assert_category("generate", address, txid, 100)
+
+ # Orphan block that paid to address
+ self.nodes[0].invalidateblock(hash)
+ # Coinbase transaction is now orphaned
+ self.assert_category("orphan", address, txid, 100)
+
+if __name__ == '__main__':
+ CoinbaseCategoryTest().main()
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index ab9ebed8d4..c514b7e0b4 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -31,12 +31,18 @@ class WalletEncryptionTest(BitcoinTestFramework):
privkey = self.nodes[0].dumpprivkey(address)
assert_equal(privkey[:1], "c")
assert_equal(len(privkey), 52)
+ assert_raises_rpc_error(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called", self.nodes[0].walletpassphrase, 'ff', 1)
+ assert_raises_rpc_error(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.", self.nodes[0].walletpassphrasechange, 'ff', 'ff')
# Encrypt the wallet
+ assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].encryptwallet, '')
self.nodes[0].encryptwallet(passphrase)
# Test that the wallet is encrypted
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
+ assert_raises_rpc_error(-15, "Error: running with an encrypted wallet, but encryptwallet was called.", self.nodes[0].encryptwallet, 'ff')
+ assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].walletpassphrase, '', 1)
+ assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].walletpassphrasechange, '', 'ff')
# Check that walletpassphrase works
self.nodes[0].walletpassphrase(passphrase, 2)
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 5c789b1c03..3492075694 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -2,9 +2,43 @@
# Copyright (c) 2014-2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test the importmulti RPC."""
-
-from test_framework import script
+"""Test the importmulti RPC.
+
+Test importmulti by generating keys on node0, importing the scriptPubKeys and
+addresses on node1 and then testing the address info for the different address
+variants.
+
+- `get_key()` and `get_multisig()` are called to generate keys on node0 and
+ return the privkeys, pubkeys and all variants of scriptPubKey and address.
+- `test_importmulti()` is called to send an importmulti call to node1, test
+ success, and (if unsuccessful) test the error code and error message returned.
+- `test_address()` is called to call getaddressinfo for an address on node1
+ and test the values returned."""
+from collections import namedtuple
+
+from test_framework.address import (
+ key_to_p2pkh,
+ key_to_p2sh_p2wpkh,
+ key_to_p2wpkh,
+ script_to_p2sh,
+ script_to_p2sh_p2wsh,
+ script_to_p2wsh,
+)
+from test_framework.script import (
+ CScript,
+ OP_0,
+ OP_2,
+ OP_3,
+ OP_CHECKMULTISIG,
+ OP_CHECKSIG,
+ OP_DUP,
+ OP_EQUAL,
+ OP_EQUALVERIFY,
+ OP_HASH160,
+ OP_NOP,
+ hash160,
+ sha256,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -13,12 +47,26 @@ from test_framework.util import (
bytes_to_hex_str,
hex_str_to_bytes
)
-from test_framework.script import (
- CScript,
- OP_0,
- hash160
-)
-from test_framework.messages import sha256
+
+Key = namedtuple('Key', ['privkey',
+ 'pubkey',
+ 'p2pkh_script',
+ 'p2pkh_addr',
+ 'p2wpkh_script',
+ 'p2wpkh_addr',
+ 'p2sh_p2wpkh_script',
+ 'p2sh_p2wpkh_redeem_script',
+ 'p2sh_p2wpkh_addr'])
+
+Multisig = namedtuple('Multisig', ['privkeys',
+ 'pubkeys',
+ 'p2sh_script',
+ 'p2sh_addr',
+ 'redeem_script',
+ 'p2wsh_script',
+ 'p2wsh_addr',
+ 'p2sh_p2wsh_script',
+ 'p2sh_p2wsh_addr'])
class ImportMultiTest(BitcoinTestFramework):
def set_test_params(self):
@@ -32,7 +80,68 @@ class ImportMultiTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- def run_test (self):
+ def get_key(self):
+ """Generate a fresh key on node0
+
+ Returns a named tuple of privkey, pubkey and all address and scripts."""
+ addr = self.nodes[0].getnewaddress()
+ pubkey = self.nodes[0].getaddressinfo(addr)['pubkey']
+ pkh = hash160(hex_str_to_bytes(pubkey))
+ return Key(self.nodes[0].dumpprivkey(addr),
+ pubkey,
+ CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(), # p2pkh
+ key_to_p2pkh(pubkey), # p2pkh addr
+ CScript([OP_0, pkh]).hex(), # p2wpkh
+ key_to_p2wpkh(pubkey), # p2wpkh addr
+ CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(), # p2sh-p2wpkh
+ CScript([OP_0, pkh]).hex(), # p2sh-p2wpkh redeem script
+ key_to_p2sh_p2wpkh(pubkey)) # p2sh-p2wpkh addr
+
+ def get_multisig(self):
+ """Generate a fresh multisig on node0
+
+ Returns a named tuple of privkeys, pubkeys and all address and scripts."""
+ addrs = []
+ pubkeys = []
+ for _ in range(3):
+ addr = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ addrs.append(addr['address'])
+ pubkeys.append(addr['pubkey'])
+ script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
+ witness_script = CScript([OP_0, sha256(script_code)])
+ return Multisig([self.nodes[0].dumpprivkey(addr) for addr in addrs],
+ pubkeys,
+ CScript([OP_HASH160, hash160(script_code), OP_EQUAL]).hex(), # p2sh
+ script_to_p2sh(script_code), # p2sh addr
+ script_code.hex(), # redeem script
+ witness_script.hex(), # p2wsh
+ script_to_p2wsh(script_code), # p2wsh addr
+ CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), # p2sh-p2wsh
+ script_to_p2sh_p2wsh(script_code)) # p2sh-p2wsh addr
+
+ def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]):
+ """Run importmulti and assert success"""
+ result = self.nodes[1].importmulti([req])
+ observed_warnings = []
+ if 'warnings' in result[0]:
+ observed_warnings = result[0]['warnings']
+ assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings)))
+ assert_equal(result[0]['success'], success)
+ if error_code is not None:
+ assert_equal(result[0]['error']['code'], error_code)
+ assert_equal(result[0]['error']['message'], error_message)
+
+ def test_address(self, address, **kwargs):
+ """Get address info for `address` and test whether the returned values are as expected."""
+ addr_info = self.nodes[1].getaddressinfo(address)
+ for key, value in kwargs.items():
+ if value is None:
+ if key in addr_info.keys():
+ raise AssertionError("key {} unexpectedly returned in getaddressinfo.".format(key))
+ elif addr_info[key] != value:
+ raise AssertionError("key {} value {} did not match expected value {}".format(key, addr_info[key], value))
+
+ def run_test(self):
self.log.info("Mining blocks...")
self.nodes[0].generate(1)
self.nodes[1].generate(1)
@@ -40,587 +149,474 @@ class ImportMultiTest(BitcoinTestFramework):
node0_address1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- #Check only one address
+ # Check only one address
assert_equal(node0_address1['ismine'], True)
- #Node 1 sync test
- assert_equal(self.nodes[1].getblockcount(),1)
+ # Node 1 sync test
+ assert_equal(self.nodes[1].getblockcount(), 1)
- #Address Test - before import
+ # Address Test - before import
address_info = self.nodes[1].getaddressinfo(node0_address1['address'])
assert_equal(address_info['iswatchonly'], False)
assert_equal(address_info['ismine'], False)
-
# RPC importmulti -----------------------------------------------
# Bitcoin Address (implicit non-internal)
self.log.info("Should import an address")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], timestamp)
- assert_equal(address_assert['ischange'], False)
- watchonly_address = address['address']
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now"},
+ True)
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=timestamp,
+ ischange=False)
+ watchonly_address = address
watchonly_timestamp = timestamp
self.log.info("Should not import an invalid address")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": "not valid address",
- },
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Invalid address')
+ self.test_importmulti({"scriptPubKey": {"address": "not valid address"},
+ "timestamp": "now"},
+ False,
+ error_code=-5,
+ error_message='Invalid address \"not valid address\"')
# ScriptPubKey + internal
self.log.info("Should import a scriptPubKey with internal flag")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "internal": True
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], timestamp)
- assert_equal(address_assert['ischange'], True)
+ key = self.get_key()
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "internal": True},
+ True)
+ self.test_address(key.p2pkh_addr,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=timestamp,
+ ischange=True)
# ScriptPubKey + internal + label
self.log.info("Should not allow a label to be specified when internal is true")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "internal": True,
- "label": "Example label"
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Internal addresses should not have a label')
+ key = self.get_key()
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "internal": True,
+ "label": "Example label"},
+ False,
+ error_code=-8,
+ error_message='Internal addresses should not have a label')
# Nonstandard scriptPubKey + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal flag")
- nonstandardScriptPubKey = address['scriptPubKey'] + bytes_to_hex_str(script.CScript([script.OP_NOP]))
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": nonstandardScriptPubKey,
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ nonstandardScriptPubKey = key.p2pkh_script + bytes_to_hex_str(CScript([OP_NOP]))
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
+ "timestamp": "now"},
+ False,
+ error_code=-8,
+ error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=False,
+ timestamp=None)
# Address + Public key + !Internal(explicit)
self.log.info("Should import an address with public key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "pubkeys": [ address['pubkey'] ],
- "internal": False
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], timestamp)
-
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "pubkeys": [key.pubkey],
+ "internal": False},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=timestamp)
# ScriptPubKey + Public key + internal
self.log.info("Should import a scriptPubKey with internal and with public key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- request = [{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "pubkeys": [ address['pubkey'] ],
- "internal": True
- }]
- result = self.nodes[1].importmulti(requests=request)
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], timestamp)
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "pubkeys": [key.pubkey],
+ "internal": True},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=timestamp)
# Nonstandard scriptPubKey + Public key + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal and with public key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- request = [{
- "scriptPubKey": nonstandardScriptPubKey,
- "timestamp": "now",
- "pubkeys": [ address['pubkey'] ]
- }]
- result = self.nodes[1].importmulti(requests=request)
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
+ "timestamp": "now",
+ "pubkeys": [key.pubkey]},
+ False,
+ error_code=-8,
+ error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=False,
+ timestamp=None)
# Address + Private key + !watchonly
self.log.info("Should import an address with private key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], True)
- assert_equal(address_assert['timestamp'], timestamp)
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "keys": [key.privkey]},
+ True)
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=True,
+ timestamp=timestamp)
self.log.info("Should not import an address with private key if is already imported")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -4)
- assert_equal(result[0]['error']['message'], 'The wallet already contains the private key for this address or script')
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "keys": [key.privkey]},
+ False,
+ error_code=-4,
+ error_message='The wallet already contains the private key for this address or script')
# Address + Private key + watchonly
- self.log.info("Should not import an address with private key and with watchonly")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address['address']) ],
- "watchonly": True
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
+ self.log.info("Should import an address with private key and with watchonly")
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "keys": [key.privkey],
+ "watchonly": True},
+ True,
+ warnings=["All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=True,
+ timestamp=timestamp)
# ScriptPubKey + Private key + internal
self.log.info("Should import a scriptPubKey with internal and with private key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address['address']) ],
- "internal": True
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], True)
- assert_equal(address_assert['timestamp'], timestamp)
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "keys": [key.privkey],
+ "internal": True},
+ True)
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=True,
+ timestamp=timestamp)
# Nonstandard scriptPubKey + Private key + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal and with private key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": nonstandardScriptPubKey,
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ key = self.get_key()
+ address = key.p2pkh_addr
+ self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
+ "timestamp": "now",
+ "keys": [key.privkey]},
+ False,
+ error_code=-8,
+ error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=False,
+ timestamp=None)
# P2SH address
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
+ multisig = self.get_multisig()
self.nodes[1].generate(100)
- self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
self.log.info("Should import a p2sh")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['isscript'], True)
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['timestamp'], timestamp)
- p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
+ "timestamp": "now"},
+ True)
+ self.test_address(multisig.p2sh_addr,
+ isscript=True,
+ iswatchonly=True,
+ timestamp=timestamp)
+ p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
assert_equal(p2shunspent['spendable'], False)
assert_equal(p2shunspent['solvable'], False)
-
# P2SH + Redeem script
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
+ multisig = self.get_multisig()
self.nodes[1].generate(100)
- self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
self.log.info("Should import a p2sh with respective redeem script")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- "redeemscript": multi_sig_script['redeemScript']
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['timestamp'], timestamp)
-
- p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
+ "timestamp": "now",
+ "redeemscript": multisig.redeem_script},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True)
+
+ p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
assert_equal(p2shunspent['spendable'], False)
assert_equal(p2shunspent['solvable'], True)
-
# P2SH + Redeem script + Private Keys + !Watchonly
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
+ multisig = self.get_multisig()
self.nodes[1].generate(100)
- self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
self.log.info("Should import a p2sh with respective redeem script and private keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- "redeemscript": multi_sig_script['redeemScript'],
- "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['timestamp'], timestamp)
-
- p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
+ "timestamp": "now",
+ "redeemscript": multisig.redeem_script,
+ "keys": multisig.privkeys[0:2]},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(multisig.p2sh_addr,
+ timestamp=timestamp,
+ ismine=False,
+ iswatchonly=True,
+ solvable=True)
+
+ p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
assert_equal(p2shunspent['spendable'], False)
assert_equal(p2shunspent['solvable'], True)
# P2SH + Redeem script + Private Keys + Watchonly
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
+ multisig = self.get_multisig()
self.nodes[1].generate(100)
- self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
self.log.info("Should import a p2sh with respective redeem script and private keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- "redeemscript": multi_sig_script['redeemScript'],
- "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])],
- "watchonly": True
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
-
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
+ "timestamp": "now",
+ "redeemscript": multisig.redeem_script,
+ "keys": multisig.privkeys[0:2],
+ "watchonly": True},
+ True)
+ self.test_address(multisig.p2sh_addr,
+ iswatchonly=True,
+ ismine=False,
+ solvable=True,
+ timestamp=timestamp)
# Address + Public key + !Internal + Wrong pubkey
- self.log.info("Should not import an address with a wrong public key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "pubkeys": [ address2['pubkey'] ]
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Key does not match address destination')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ self.log.info("Should not import an address with the wrong public key as non-solvable")
+ key = self.get_key()
+ address = key.p2pkh_addr
+ wrong_key = self.get_key().pubkey
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "pubkeys": [wrong_key]},
+ True,
+ warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ solvable=False,
+ timestamp=timestamp)
# ScriptPubKey + Public key + internal + Wrong pubkey
- self.log.info("Should not import a scriptPubKey with internal and with a wrong public key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- request = [{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "pubkeys": [ address2['pubkey'] ],
- "internal": True
- }]
- result = self.nodes[1].importmulti(request)
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Key does not match address destination')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ self.log.info("Should import a scriptPubKey with internal and with a wrong public key as non-solvable")
+ key = self.get_key()
+ address = key.p2pkh_addr
+ wrong_key = self.get_key().pubkey
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "pubkeys": [wrong_key],
+ "internal": True},
+ True,
+ warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ solvable=False,
+ timestamp=timestamp)
# Address + Private key + !watchonly + Wrong private key
- self.log.info("Should not import an address with a wrong private key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address2['address']) ]
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Key does not match address destination')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ self.log.info("Should import an address with a wrong private key as non-solvable")
+ key = self.get_key()
+ address = key.p2pkh_addr
+ wrong_privkey = self.get_key().privkey
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "keys": [wrong_privkey]},
+ True,
+ warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ solvable=False,
+ timestamp=timestamp)
# ScriptPubKey + Private key + internal + Wrong private key
- self.log.info("Should not import a scriptPubKey with internal and with a wrong private key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- result = self.nodes[1].importmulti([{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "now",
- "keys": [ self.nodes[0].dumpprivkey(address2['address']) ],
- "internal": True
- }])
- assert_equal(result[0]['success'], False)
- assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Key does not match address destination')
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], False)
- assert_equal('timestamp' in address_assert, False)
-
+ self.log.info("Should import a scriptPubKey with internal and with a wrong private key as non-solvable")
+ key = self.get_key()
+ address = key.p2pkh_addr
+ wrong_privkey = self.get_key().privkey
+ self.test_importmulti({"scriptPubKey": key.p2pkh_script,
+ "timestamp": "now",
+ "keys": [wrong_privkey],
+ "internal": True},
+ True,
+ warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ iswatchonly=True,
+ ismine=False,
+ solvable=False,
+ timestamp=timestamp)
# Importing existing watch only address with new timestamp should replace saved timestamp.
assert_greater_than(timestamp, watchonly_timestamp)
self.log.info("Should replace previously saved watch only timestamp.")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": watchonly_address,
- },
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(watchonly_address)
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], timestamp)
+ self.test_importmulti({"scriptPubKey": {"address": watchonly_address},
+ "timestamp": "now"},
+ True)
+ self.test_address(watchonly_address,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=timestamp)
watchonly_timestamp = timestamp
-
# restart nodes to check for proper serialization/deserialization of watch only address
self.stop_nodes()
self.start_nodes()
- address_assert = self.nodes[1].getaddressinfo(watchonly_address)
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], watchonly_timestamp)
+ self.test_address(watchonly_address,
+ iswatchonly=True,
+ ismine=False,
+ timestamp=watchonly_timestamp)
# Bad or missing timestamps
self.log.info("Should throw on invalid or missing timestamp values")
assert_raises_rpc_error(-3, 'Missing required timestamp field for key',
- self.nodes[1].importmulti, [{
- "scriptPubKey": address['scriptPubKey'],
- }])
+ self.nodes[1].importmulti, [{"scriptPubKey": key.p2pkh_script}])
assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string',
- self.nodes[1].importmulti, [{
- "scriptPubKey": address['scriptPubKey'],
- "timestamp": "",
- }])
+ self.nodes[1].importmulti, [{
+ "scriptPubKey": key.p2pkh_script,
+ "timestamp": ""
+ }])
# Import P2WPKH address as watch only
self.log.info("Should import a P2WPKH address as watch only")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], True)
- assert_equal(address_assert['solvable'], False)
+ key = self.get_key()
+ address = key.p2wpkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now"},
+ True)
+ self.test_address(address,
+ iswatchonly=True,
+ solvable=False)
# Import P2WPKH address with public key but no private key
self.log.info("Should import a P2WPKH address and public key as solvable but not spendable")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "pubkeys": [ address['pubkey'] ]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['solvable'], True)
+ key = self.get_key()
+ address = key.p2wpkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "pubkeys": [key.pubkey]},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ ismine=False,
+ solvable=True)
# Import P2WPKH address with key and check it is spendable
self.log.info("Should import a P2WPKH address with key")
- address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": address['address']
- },
- "timestamp": "now",
- "keys": [self.nodes[0].dumpprivkey(address['address'])]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(address['address'])
- assert_equal(address_assert['iswatchonly'], False)
- assert_equal(address_assert['ismine'], True)
+ key = self.get_key()
+ address = key.p2wpkh_addr
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "keys": [key.privkey]},
+ True)
+ self.test_address(address,
+ iswatchonly=False,
+ ismine=True)
# P2WSH multisig address without scripts or keys
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].addmultisigaddress(2, [sig_address_1['pubkey'], sig_address_2['pubkey']], "", "bech32")
+ multisig = self.get_multisig()
self.log.info("Should import a p2wsh multisig as watch only without respective redeem script and private keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now"
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['solvable'], False)
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2wsh_addr},
+ "timestamp": "now"},
+ True)
+ self.test_address(multisig.p2sh_addr,
+ solvable=False)
# Same P2WSH multisig address as above, but now with witnessscript + private keys
- self.log.info("Should import a p2wsh with respective redeem script and private keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- "witnessscript": multi_sig_script['redeemScript'],
- "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address']) ]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['solvable'], True)
- assert_equal(address_assert['ismine'], True)
- assert_equal(address_assert['sigsrequired'], 2)
+ self.log.info("Should import a p2wsh with respective witness script and private keys")
+ self.test_importmulti({"scriptPubKey": {"address": multisig.p2wsh_addr},
+ "timestamp": "now",
+ "witnessscript": multisig.redeem_script,
+ "keys": multisig.privkeys},
+ True)
+ self.test_address(multisig.p2sh_addr,
+ solvable=True,
+ ismine=True,
+ sigsrequired=2)
# P2SH-P2WPKH address with no redeemscript or public or private key
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
- pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
- pkscript = CScript([OP_0, pubkeyhash])
+ key = self.get_key()
+ address = key.p2sh_p2wpkh_addr
self.log.info("Should import a p2sh-p2wpkh without redeem script or keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": sig_address_1['address']
- },
- "timestamp": "now"
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
- assert_equal(address_assert['solvable'], False)
- assert_equal(address_assert['ismine'], False)
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now"},
+ True)
+ self.test_address(address,
+ solvable=False,
+ ismine=False)
# P2SH-P2WPKH address + redeemscript + public key with no private key
self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": sig_address_1['address']
- },
- "timestamp": "now",
- "redeemscript": bytes_to_hex_str(pkscript),
- "pubkeys": [ sig_address_1['pubkey'] ]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
- assert_equal(address_assert['solvable'], True)
- assert_equal(address_assert['ismine'], False)
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "redeemscript": key.p2sh_p2wpkh_redeem_script,
+ "pubkeys": [key.pubkey]},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ solvable=True,
+ ismine=False)
# P2SH-P2WPKH address + redeemscript + private key
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
- pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
- pkscript = CScript([OP_0, pubkeyhash])
+ key = self.get_key()
+ address = key.p2sh_p2wpkh_addr
self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": sig_address_1['address']
- },
- "timestamp": "now",
- "redeemscript": bytes_to_hex_str(pkscript),
- "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address'])]
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
- assert_equal(address_assert['solvable'], True)
- assert_equal(address_assert['ismine'], True)
-
- # P2SH-P2WSH 1-of-1 multisig + redeemscript with no private key
- sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
- multi_sig_script = self.nodes[0].addmultisigaddress(1, [sig_address_1['pubkey']], "", "p2sh-segwit")
- scripthash = sha256(hex_str_to_bytes(multi_sig_script['redeemScript']))
- redeem_script = CScript([OP_0, scripthash])
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "redeemscript": key.p2sh_p2wpkh_redeem_script,
+ "keys": [key.privkey]},
+ True)
+ self.test_address(address,
+ solvable=True,
+ ismine=True)
+
+ # P2SH-P2WSH multisig + redeemscript with no private key
+ multisig = self.get_multisig()
+ address = multisig.p2sh_p2wsh_addr
self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key")
- result = self.nodes[1].importmulti([{
- "scriptPubKey": {
- "address": multi_sig_script['address']
- },
- "timestamp": "now",
- "redeemscript": bytes_to_hex_str(redeem_script),
- "witnessscript": multi_sig_script['redeemScript']
- }])
- assert_equal(result[0]['success'], True)
- address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
- assert_equal(address_assert['solvable'], True)
+ self.test_importmulti({"scriptPubKey": {"address": address},
+ "timestamp": "now",
+ "redeemscript": multisig.p2wsh_script,
+ "witnessscript": multisig.redeem_script},
+ True,
+ warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
+ self.test_address(address,
+ solvable=True,
+ ismine=False)
if __name__ == '__main__':
- ImportMultiTest ().main ()
+ ImportMultiTest().main()
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index f1a441c399..b7c8d3098d 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -24,8 +24,8 @@ from test_framework.util import (
class KeypoolRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 2
- self.extra_args = [[], ['-keypool=100']]
+ self.num_nodes = 4
+ self.extra_args = [[], ['-keypool=100'], ['-keypool=100'], ['-keypool=100']]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -40,32 +40,47 @@ class KeypoolRestoreTest(BitcoinTestFramework):
shutil.copyfile(wallet_path, wallet_backup_path)
self.start_node(1, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 0, 2)
+ connect_nodes_bi(self.nodes, 0, 3)
- self.log.info("Generate keys for wallet")
- for _ in range(90):
- addr_oldpool = self.nodes[1].getnewaddress()
- for _ in range(20):
- addr_extpool = self.nodes[1].getnewaddress()
+ for i, output_type in enumerate(["legacy", "p2sh-segwit", "bech32"]):
- self.log.info("Send funds to wallet")
- self.nodes[0].sendtoaddress(addr_oldpool, 10)
- self.nodes[0].generate(1)
- self.nodes[0].sendtoaddress(addr_extpool, 5)
- self.nodes[0].generate(1)
- sync_blocks(self.nodes)
+ self.log.info("Generate keys for wallet with address type: {}".format(output_type))
+ idx = i+1
+ for _ in range(90):
+ addr_oldpool = self.nodes[idx].getnewaddress(address_type=output_type)
+ for _ in range(20):
+ addr_extpool = self.nodes[idx].getnewaddress(address_type=output_type)
- self.log.info("Restart node with wallet backup")
- self.stop_node(1)
- shutil.copyfile(wallet_backup_path, wallet_path)
- self.start_node(1, self.extra_args[1])
- connect_nodes_bi(self.nodes, 0, 1)
- self.sync_all()
+ # Make sure we're creating the outputs we expect
+ address_details = self.nodes[idx].validateaddress(addr_extpool)
+ if i == 0:
+ assert(not address_details["isscript"] and not address_details["iswitness"])
+ elif i == 1:
+ assert(address_details["isscript"] and not address_details["iswitness"])
+ else:
+ assert(not address_details["isscript"] and address_details["iswitness"])
+
+
+ self.log.info("Send funds to wallet")
+ self.nodes[0].sendtoaddress(addr_oldpool, 10)
+ self.nodes[0].generate(1)
+ self.nodes[0].sendtoaddress(addr_extpool, 5)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+ self.log.info("Restart node with wallet backup")
+ self.stop_node(idx)
+ shutil.copyfile(wallet_backup_path, wallet_path)
+ self.start_node(idx, self.extra_args[idx])
+ connect_nodes_bi(self.nodes, 0, idx)
+ self.sync_all()
- self.log.info("Verify keypool is restored and balance is correct")
- assert_equal(self.nodes[1].getbalance(), 15)
- assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive")
- # Check that we have marked all keys up to the used keypool key as used
- assert_equal(self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'")
+ self.log.info("Verify keypool is restored and balance is correct")
+ assert_equal(self.nodes[idx].getbalance(), 15)
+ assert_equal(self.nodes[idx].listtransactions()[0]['category'], "receive")
+ # Check that we have marked all keys up to the used keypool key as used
+ assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress())['hdkeypath'], "m/0'/0'/110'")
if __name__ == '__main__':
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 4f663c82c7..8ab569a3c3 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -223,6 +223,9 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load duplicate wallets
assert_raises_rpc_error(-4, 'Wallet file verification failed: Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
+ # Fail to load duplicate wallets by different ways (directory and filepath)
+ assert_raises_rpc_error(-4, "Wallet file verification failed: Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
+
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index b0d9f87958..4facd6c334 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -26,8 +26,12 @@ SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb'])
def main():
- used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True, encoding='utf8')
- docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True, encoding='utf8')
+ if sys.version_info >= (3, 6):
+ used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True, encoding='utf8')
+ docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True, encoding='utf8')
+ else:
+ used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True) # encoding='utf8'
+ docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True) # encoding='utf8'
args_used = set(re.findall(re.compile(REGEX_ARG), used))
args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL)
diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh
index f1327469f3..4267f9fa0d 100755
--- a/test/lint/commit-script-check.sh
+++ b/test/lint/commit-script-check.sh
@@ -20,23 +20,23 @@ fi
RET=0
PREV_BRANCH=`git name-rev --name-only HEAD`
PREV_HEAD=`git rev-parse HEAD`
-for i in `git rev-list --reverse $1`; do
- if git rev-list -n 1 --pretty="%s" $i | grep -q "^scripted-diff:"; then
- git checkout --quiet $i^ || exit
- SCRIPT="`git rev-list --format=%b -n1 $i | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d'`"
+for commit in `git rev-list --reverse $1`; do
+ if git rev-list -n 1 --pretty="%s" $commit | grep -q "^scripted-diff:"; then
+ git checkout --quiet $commit^ || exit
+ SCRIPT="`git rev-list --format=%b -n1 $commit | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d'`"
if test "x$SCRIPT" = "x"; then
- echo "Error: missing script for: $i"
+ echo "Error: missing script for: $commit"
echo "Failed"
RET=1
else
- echo "Running script for: $i"
+ echo "Running script for: $commit"
echo "$SCRIPT"
- eval "$SCRIPT"
- git --no-pager diff --exit-code $i && echo "OK" || (echo "Failed"; false) || RET=1
+ (eval "$SCRIPT")
+ git --no-pager diff --exit-code $commit && echo "OK" || (echo "Failed"; false) || RET=1
fi
git reset --quiet --hard HEAD
else
- if git rev-list "--format=%b" -n1 $i | grep -q '^-\(BEGIN\|END\)[ a-zA-Z]*-$'; then
+ if git rev-list "--format=%b" -n1 $commit | grep -q '^-\(BEGIN\|END\)[ a-zA-Z]*-$'; then
echo "Error: script block marker but no scripted-diff in title"
echo "Failed"
RET=1
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 5caebf3739..f5d4780b6d 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -241,12 +241,11 @@ def count_format_specifiers(format_string):
4
"""
assert(type(format_string) is str)
+ format_string = format_string.replace('%%', 'X')
n = 0
in_specifier = False
for i, char in enumerate(format_string):
- if format_string[i - 1:i + 1] == "%%" or format_string[i:i + 2] == "%%":
- pass
- elif char == "%":
+ if char == "%":
in_specifier = True
n += 1
elif char in "aAcdeEfFgGinopsuxX":
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index f6d0fd382b..4b9e2615b6 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -50,7 +50,6 @@ EXPECTED_BOOST_INCLUDES=(
boost/algorithm/string/classification.hpp
boost/algorithm/string/replace.hpp
boost/algorithm/string/split.hpp
- boost/bind.hpp
boost/chrono/chrono.hpp
boost/date_time/posix_time/posix_time.hpp
boost/filesystem.hpp
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
index 3341f794f9..4561b0db30 100755
--- a/test/lint/lint-python-dead-code.sh
+++ b/test/lint/lint-python-dead-code.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index d44a585294..3dbb9fff28 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -36,6 +36,7 @@ export LC_ALL=C
# E701 multiple statements on one line (colon)
# E702 multiple statements on one line (semicolon)
# E703 statement ends with a semicolon
+# E711 comparison to None should be 'if cond is None:'
# E714 test for object identity should be "is not"
# E721 do not compare types, use "isinstance()"
# E741 do not use variables named "l", "O", or "I"
@@ -87,4 +88,4 @@ elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then
exit 0
fi
-PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 "${@:-.}"
+PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E711,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 "${@:-.}"
diff --git a/test/lint/lint-rpc-help.sh b/test/lint/lint-rpc-help.sh
new file mode 100755
index 0000000000..faac5d43e2
--- /dev/null
+++ b/test/lint/lint-rpc-help.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check that all RPC help texts are generated by RPCHelpMan.
+
+export LC_ALL=C
+
+EXIT_CODE=0
+
+# Assume that all multiline strings passed into a runtime_error are help texts.
+# This is potentially fragile, but the linter is only temporary and can safely
+# be removed early 2019.
+
+non_autogenerated_help=$(grep --perl-regexp --null-data --only-matching 'runtime_error\(\n\s*".*\\n"\n' $(git ls-files -- "*.cpp"))
+if [[ ${non_autogenerated_help} != "" ]]; then
+ echo "Must use RPCHelpMan to generate the help for the following RPC methods:"
+ echo "${non_autogenerated_help}"
+ echo
+ EXIT_CODE=1
+fi
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-python-shebang.sh b/test/lint/lint-shebang.sh
index 4ff87f0bf7..fda22592d3 100755
--- a/test/lint/lint-python-shebang.sh
+++ b/test/lint/lint-shebang.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# Shebang must use python3 (not python or python2)
+# Assert expected shebang lines
export LC_ALL=C
EXIT_CODE=0
@@ -10,4 +10,11 @@ for PYTHON_FILE in $(git ls-files -- "*.py"); do
EXIT_CODE=1
fi
done
+for SHELL_FILE in $(git ls-files -- "*.sh"); do
+ if [[ $(head -n 1 "${SHELL_FILE}") != "#!/usr/bin/env bash" &&
+ $(head -n 1 "${SHELL_FILE}") != "#!/bin/sh" ]]; then
+ echo "Missing expected shebang \"#!/usr/bin/env bash\" or \"#!/bin/sh\" in ${SHELL_FILE}"
+ EXIT_CODE=1
+ fi
+done
exit ${EXIT_CODE}
diff --git a/test/sanitizer_suppressions/lsan b/test/sanitizer_suppressions/lsan
new file mode 100644
index 0000000000..6f15c0f1d4
--- /dev/null
+++ b/test/sanitizer_suppressions/lsan
@@ -0,0 +1,6 @@
+# Suppress warnings triggered in dependencies
+leak:libcrypto
+leak:libqminimal
+leak:libQt5Core
+leak:libQt5Gui
+leak:libQt5Widgets
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
new file mode 100644
index 0000000000..70eea34363
--- /dev/null
+++ b/test/sanitizer_suppressions/tsan
@@ -0,0 +1,16 @@
+# ThreadSanitizer suppressions
+# ============================
+
+# WalletBatch (unidentified deadlock)
+deadlock:WalletBatch
+
+# Intentional deadlock in tests
+deadlock:TestPotentialDeadLockDetected
+
+# Wildcard for all gui tests, should be replaced with non-wildcard suppressions
+race:src/qt/test/*
+deadlock:src/qt/test/*
+
+# External libraries
+deadlock:libdb
+race:libzmq
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
new file mode 100644
index 0000000000..e90d5c2ac0
--- /dev/null
+++ b/test/sanitizer_suppressions/ubsan
@@ -0,0 +1,36 @@
+alignment:move.h
+alignment:prevector.h
+bool:wallet/wallet.cpp
+float-divide-by-zero:policy/fees.cpp
+float-divide-by-zero:validation.cpp
+float-divide-by-zero:wallet/wallet.cpp
+nonnull-attribute:support/cleanse.cpp
+unsigned-integer-overflow:arith_uint256.h
+unsigned-integer-overflow:basic_string.h
+unsigned-integer-overflow:bench/bench.h
+unsigned-integer-overflow:bitcoin-tx.cpp
+unsigned-integer-overflow:bloom.cpp
+unsigned-integer-overflow:chain.cpp
+unsigned-integer-overflow:chain.h
+unsigned-integer-overflow:coded_stream.h
+unsigned-integer-overflow:core_write.cpp
+unsigned-integer-overflow:crypto/chacha20.cpp
+unsigned-integer-overflow:crypto/ctaes/ctaes.c
+unsigned-integer-overflow:crypto/ripemd160.cpp
+unsigned-integer-overflow:crypto/sha1.cpp
+unsigned-integer-overflow:crypto/sha256.cpp
+unsigned-integer-overflow:crypto/sha512.cpp
+unsigned-integer-overflow:hash.cpp
+unsigned-integer-overflow:leveldb/db/log_reader.cc
+unsigned-integer-overflow:leveldb/util/bloom.cc
+unsigned-integer-overflow:leveldb/util/crc32c.h
+unsigned-integer-overflow:leveldb/util/hash.cc
+unsigned-integer-overflow:policy/fees.cpp
+unsigned-integer-overflow:prevector.h
+unsigned-integer-overflow:script/interpreter.cpp
+unsigned-integer-overflow:stl_bvector.h
+unsigned-integer-overflow:streams.h
+unsigned-integer-overflow:txmempool.cpp
+unsigned-integer-overflow:util/strencodings.cpp
+unsigned-integer-overflow:validation.cpp
+vptr:fs.cpp
diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py
index 92fef30e13..7b1cc2b031 100755
--- a/test/util/bitcoin-util-test.py
+++ b/test/util/bitcoin-util-test.py
@@ -9,14 +9,9 @@ Runs automatically during `make check`.
Can also be run manually."""
-from __future__ import division,print_function,unicode_literals
-
import argparse
import binascii
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
+import configparser
import difflib
import json
import logging
diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py
index 46e9fbc739..53058dc394 100755
--- a/test/util/rpcauth-test.py
+++ b/test/util/rpcauth-test.py
@@ -24,8 +24,8 @@ class TestRPCAuth(unittest.TestCase):
self.rpcauth = importlib.import_module('rpcauth')
def test_generate_salt(self):
- self.assertLessEqual(len(self.rpcauth.generate_salt()), 32)
- self.assertGreaterEqual(len(self.rpcauth.generate_salt()), 16)
+ for i in range(16, 32 + 1):
+ self.assertEqual(len(self.rpcauth.generate_salt(i)), i * 2)
def test_generate_password(self):
password = self.rpcauth.generate_password()
@@ -34,7 +34,7 @@ class TestRPCAuth(unittest.TestCase):
self.assertEqual(expected_password, password)
def test_check_password_hmac(self):
- salt = self.rpcauth.generate_salt()
+ salt = self.rpcauth.generate_salt(16)
password = self.rpcauth.generate_password()
password_hmac = self.rpcauth.password_to_hmac(salt, password)