aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/config.ini.in1
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py55
-rwxr-xr-xtest/functional/feature_config_args.py51
-rwxr-xr-xtest/functional/feature_proxy.py41
-rwxr-xr-xtest/functional/interface_zmq.py42
-rwxr-xr-xtest/functional/mocks/signer.py102
-rwxr-xr-xtest/functional/p2p_invalid_tx.py2
-rwxr-xr-xtest/functional/rpc_blockchain.py19
-rwxr-xr-xtest/functional/rpc_getblockfilter.py6
-rwxr-xr-xtest/functional/rpc_help.py5
-rwxr-xr-xtest/functional/rpc_net.py4
-rwxr-xr-xtest/functional/rpc_signmessage.py22
-rwxr-xr-xtest/functional/test_framework/test_framework.py17
-rwxr-xr-xtest/functional/test_framework/test_node.py4
-rw-r--r--test/functional/test_framework/util.py1
-rw-r--r--test/functional/test_framework/wallet.py9
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_listdescriptors.py16
-rwxr-xr-xtest/functional/wallet_signer.py217
-rwxr-xr-xtest/functional/wallet_txn_clone.py7
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py7
21 files changed, 547 insertions, 82 deletions
diff --git a/test/config.ini.in b/test/config.ini.in
index 77c9a720c3..e3872181cd 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -23,3 +23,4 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true
@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true
+@ENABLE_EXTERNAL_SIGNER_TRUE@ENABLE_EXTERNAL_SIGNER=true
diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py
index 455073ef9c..d13d191b20 100755
--- a/test/functional/feature_blockfilterindex_prune.py
+++ b/test/functional/feature_blockfilterindex_prune.py
@@ -5,49 +5,60 @@
"""Test blockfilterindex in conjunction with prune."""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
- assert_raises_rpc_error,
+ assert_equal,
assert_greater_than,
+ assert_raises_rpc_error,
)
class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 2
- self.extra_args = [["-fastprune", "-prune=1"], ["-fastprune", "-prune=1", "-blockfilterindex=1"]]
+ self.num_nodes = 1
+ self.extra_args = [["-fastprune", "-prune=1", "-blockfilterindex=1"]]
+
+ def sync_index(self, height):
+ expected = {'basic block filter index': {'synced': True, 'best_block_height': height}}
+ self.wait_until(lambda: self.nodes[0].getindexinfo() == expected)
def run_test(self):
- # test basic pruning compatibility & filter access of pruned blocks
self.log.info("check if we can access a blockfilter when pruning is enabled but no blocks are actually pruned")
- assert len(self.nodes[1].getblockfilter(self.nodes[1].getbestblockhash())['filter']) > 0
+ self.sync_index(height=200)
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
# Mine two batches of blocks to avoid hitting NODE_NETWORK_LIMITED_MIN_BLOCKS disconnection
- self.nodes[1].generate(250)
+ self.nodes[0].generate(250)
self.sync_all()
- self.nodes[1].generate(250)
+ self.nodes[0].generate(250)
self.sync_all()
+ self.sync_index(height=700)
+
self.log.info("prune some blocks")
- pruneheight = self.nodes[1].pruneblockchain(400)
- assert pruneheight != 0
+ pruneheight = self.nodes[0].pruneblockchain(400)
+ assert_equal(pruneheight, 250)
+
self.log.info("check if we can access the tips blockfilter when we have pruned some blocks")
- assert len(self.nodes[1].getblockfilter(self.nodes[1].getbestblockhash())['filter']) > 0
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
+
self.log.info("check if we can access the blockfilter of a pruned block")
- assert len(self.nodes[1].getblockfilter(self.nodes[1].getblockhash(2))['filter']) > 0
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getblockhash(2))['filter']), 0)
+
self.log.info("start node without blockfilterindex")
- self.stop_node(1)
- self.start_node(1, extra_args=self.extra_args[0])
+ self.restart_node(0, extra_args=["-fastprune", "-prune=1"])
+
self.log.info("make sure accessing the blockfilters throws an error")
- assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[1].getblockfilter, self.nodes[1].getblockhash(2))
- self.nodes[1].generate(1000)
+ assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[0].getblockfilter, self.nodes[0].getblockhash(2))
+ self.nodes[0].generate(1000)
+
self.log.info("prune below the blockfilterindexes best block while blockfilters are disabled")
- pruneheight_new = self.nodes[1].pruneblockchain(1000)
+ pruneheight_new = self.nodes[0].pruneblockchain(1000)
assert_greater_than(pruneheight_new, pruneheight)
- self.stop_node(1)
+ self.stop_node(0)
+
self.log.info("make sure we get an init error when starting the node again with block filters")
- with self.nodes[1].assert_debug_log(["basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"]):
- self.nodes[1].assert_start_raises_init_error(extra_args=self.extra_args[1])
+ with self.nodes[0].assert_debug_log(["basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"]):
+ self.nodes[0].assert_start_raises_init_error(extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"])
+
self.log.info("make sure the node starts again with the -reindex arg")
- reindex_args = self.extra_args[1]
- reindex_args.append("-reindex")
- self.start_node(1, extra_args=reindex_args)
+ self.start_node(0, extra_args = ["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"])
if __name__ == '__main__':
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 573760a8cb..a0bcd9f12a 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -19,7 +19,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.wallet_names = []
def test_config_file_parser(self):
- # Assume node is stopped
+ self.stop_node(0)
inc_conf_file_path = os.path.join(self.nodes[0].datadir, 'include.conf')
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
@@ -89,11 +89,12 @@ class ConfArgsTest(BitcoinTestFramework):
)
def test_log_buffer(self):
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0\n']):
self.start_node(0, extra_args=['-noconnect=0'])
- self.stop_node(0)
def test_args_log(self):
+ self.stop_node(0)
self.log.info('Test config args logging')
with self.nodes[0].assert_debug_log(
expected_msgs=[
@@ -120,37 +121,41 @@ class ConfArgsTest(BitcoinTestFramework):
'-rpcuser=secret-rpcuser',
'-torpassword=secret-torpassword',
])
- self.stop_node(0)
def test_networkactive(self):
self.log.info('Test -networkactive option')
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0)
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0, extra_args=['-networkactive'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0, extra_args=['-networkactive=1'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-networkactive=0'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-nonetworkactive'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-nonetworkactive=1'])
- self.stop_node(0)
def test_seed_peers(self):
self.log.info('Test seed peers')
default_data_dir = self.nodes[0].datadir
+ # Only regtest has no fixed seeds. To avoid connections to random
+ # nodes, regtest is the only network where it is safe to enable
+ # -fixedseeds in tests
+ util.assert_equal(self.nodes[0].getblockchaininfo()['chain'],'regtest')
+ self.stop_node(0)
# No peers.dat exists and -dnsseed=1
# We expect the node will use DNS Seeds, but Regtest mode has 0 DNS seeds
@@ -159,10 +164,12 @@ class ConfArgsTest(BitcoinTestFramework):
start = int(time.time())
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
- "0 addresses found from DNS seeds"]):
- self.start_node(0, extra_args=['-dnsseed=1 -mocktime={}'.format(start)])
+ "0 addresses found from DNS seeds",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=1', '-fixedseeds=1', f'-mocktime={start}'])
with self.nodes[0].assert_debug_log(expected_msgs=[
- "Adding fixed seeds as 60 seconds have passed and addrman is empty"]):
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty",
+ ]):
self.nodes[0].setmocktime(start + 65)
self.stop_node(0)
@@ -173,8 +180,9 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"DNS seeding disabled",
- "Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n"]):
- self.start_node(0, extra_args=['-dnsseed=0'])
+ "Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1'])
assert time.time() - start < 60
self.stop_node(0)
@@ -185,7 +193,8 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"DNS seeding disabled",
- "Fixed seeds are disabled"]):
+ "Fixed seeds are disabled",
+ ]):
self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=0'])
assert time.time() - start < 60
self.stop_node(0)
@@ -196,17 +205,15 @@ class ConfArgsTest(BitcoinTestFramework):
start = int(time.time())
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
- "DNS seeding disabled"]):
- self.start_node(0, extra_args=['-dnsseed=0', '-addnode=fakenodeaddr -mocktime={}'.format(start)])
+ "DNS seeding disabled",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1', '-addnode=fakenodeaddr', f'-mocktime={start}'])
with self.nodes[0].assert_debug_log(expected_msgs=[
- "Adding fixed seeds as 60 seconds have passed and addrman is empty"]):
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty",
+ ]):
self.nodes[0].setmocktime(start + 65)
- self.stop_node(0)
-
def run_test(self):
- self.stop_node(0)
-
self.test_log_buffer()
self.test_args_log()
self.test_seed_peers()
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 2983feaa0d..8bee43b8ad 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -49,9 +49,10 @@ NET_UNROUTABLE = "not_publicly_routable"
NET_IPV4 = "ipv4"
NET_IPV6 = "ipv6"
NET_ONION = "onion"
+NET_I2P = "i2p"
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
-NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION})
+NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P})
class ProxyTest(BitcoinTestFramework):
@@ -90,11 +91,15 @@ class ProxyTest(BitcoinTestFramework):
self.serv3 = Socks5Server(self.conf3)
self.serv3.start()
+ # We will not try to connect to this.
+ self.i2p_sam = ('127.0.0.1', 7656)
+
# Note: proxies are not used to connect to local nodes. This is because the proxy to
# use is based on CService.GetNetwork(), which returns NET_UNROUTABLE for localhost.
args = [
['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
- ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
+ ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),
+ '-i2psam=%s:%i' % (self.i2p_sam), '-i2pacceptincoming=0', '-proxyrandomize=0'],
['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
[]
]
@@ -199,9 +204,16 @@ class ProxyTest(BitcoinTestFramework):
n0 = networks_dict(self.nodes[0].getnetworkinfo())
assert_equal(NETWORKS, n0.keys())
for net in NETWORKS:
- assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr))
- assert_equal(n0[net]['proxy_randomize_credentials'], True)
+ if net == NET_I2P:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = '%s:%i' % (self.conf1.addr)
+ expected_randomize = True
+ assert_equal(n0[net]['proxy'], expected_proxy)
+ assert_equal(n0[net]['proxy_randomize_credentials'], expected_randomize)
assert_equal(n0['onion']['reachable'], True)
+ assert_equal(n0['i2p']['reachable'], False)
n1 = networks_dict(self.nodes[1].getnetworkinfo())
assert_equal(NETWORKS, n1.keys())
@@ -211,21 +223,36 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr))
assert_equal(n1['onion']['proxy_randomize_credentials'], False)
assert_equal(n1['onion']['reachable'], True)
+ assert_equal(n1['i2p']['proxy'], '%s:%i' % (self.i2p_sam))
+ assert_equal(n1['i2p']['proxy_randomize_credentials'], False)
+ assert_equal(n1['i2p']['reachable'], True)
n2 = networks_dict(self.nodes[2].getnetworkinfo())
assert_equal(NETWORKS, n2.keys())
for net in NETWORKS:
- assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr))
- assert_equal(n2[net]['proxy_randomize_credentials'], True)
+ if net == NET_I2P:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = '%s:%i' % (self.conf2.addr)
+ expected_randomize = True
+ assert_equal(n2[net]['proxy'], expected_proxy)
+ assert_equal(n2[net]['proxy_randomize_credentials'], expected_randomize)
assert_equal(n2['onion']['reachable'], True)
+ assert_equal(n2['i2p']['reachable'], False)
if self.have_ipv6:
n3 = networks_dict(self.nodes[3].getnetworkinfo())
assert_equal(NETWORKS, n3.keys())
for net in NETWORKS:
- assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
+ if net == NET_I2P:
+ expected_proxy = ''
+ else:
+ expected_proxy = '[%s]:%i' % (self.conf3.addr)
+ assert_equal(n3[net]['proxy'], expected_proxy)
assert_equal(n3[net]['proxy_randomize_credentials'], False)
assert_equal(n3['onion']['reachable'], False)
+ assert_equal(n3['i2p']['reachable'], False)
if __name__ == '__main__':
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index d0967a9340..94e162b748 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -62,6 +62,31 @@ class ZMQSubscriber:
return (hash, label, mempool_sequence)
+class ZMQTestSetupBlock:
+ """Helper class for setting up a ZMQ test via the "sync up" procedure.
+ Generates a block on the specified node on instantiation and provides a
+ method to check whether a ZMQ notification matches, i.e. the event was
+ caused by this generated block. Assumes that a notification either contains
+ the generated block's hash, it's (coinbase) transaction id, the raw block or
+ raw transaction data.
+ """
+
+ def __init__(self, node):
+ self.block_hash = node.generate(1)[0]
+ coinbase = node.getblock(self.block_hash, 2)['tx'][0]
+ self.tx_hash = coinbase['txid']
+ self.raw_tx = coinbase['hex']
+ self.raw_block = node.getblock(self.block_hash, 0)
+
+ def caused_notification(self, notification):
+ return (
+ self.block_hash in notification
+ or self.tx_hash in notification
+ or self.raw_block in notification
+ or self.raw_tx in notification
+ )
+
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
@@ -105,17 +130,18 @@ class ZMQTest (BitcoinTestFramework):
# Ensure that all zmq publisher notification interfaces are ready by
# running the following "sync up" procedure:
# 1. Generate a block on the node
- # 2. Try to receive a notification on all subscribers
- # 3. If all subscribers get a message within the timeout (1 second),
+ # 2. Try to receive the corresponding notification on all subscribers
+ # 3. If all subscribers get the message within the timeout (1 second),
# we are done, otherwise repeat starting from step 1
for sub in subscribers:
sub.socket.set(zmq.RCVTIMEO, 1000)
while True:
- self.nodes[0].generate(1)
+ test_block = ZMQTestSetupBlock(self.nodes[0])
recv_failed = False
for sub in subscribers:
try:
- sub.receive()
+ while not test_block.caused_notification(sub.receive().hex()):
+ self.log.debug("Ignoring sync-up notification for previously generated block.")
except zmq.error.Again:
self.log.debug("Didn't receive sync-up notification, trying again.")
recv_failed = True
@@ -340,7 +366,7 @@ class ZMQTest (BitcoinTestFramework):
block_count = self.nodes[0].getblockcount()
best_hash = self.nodes[0].getbestblockhash()
self.nodes[0].invalidateblock(best_hash)
- sleep(2) # Bit of room to make sure transaction things happened
+ sleep(2) # Bit of room to make sure transaction things happened
# Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
# of the time they were gathered.
@@ -389,8 +415,8 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(label, "A")
# More transactions to be simply mined
for i in range(len(more_tx)):
- assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
+ assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
# Bumped by rbf
assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
mempool_seq += 1
@@ -405,7 +431,7 @@ class ZMQTest (BitcoinTestFramework):
assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
mempool_seq += 1
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
- self.sync_all() # want to make sure we didn't break "consensus" for other tests
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
def test_mempool_sync(self):
"""
diff --git a/test/functional/mocks/signer.py b/test/functional/mocks/signer.py
new file mode 100755
index 0000000000..676d0a0a4d
--- /dev/null
+++ b/test/functional/mocks/signer.py
@@ -0,0 +1,102 @@
+#!/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.
+
+import os
+import sys
+import argparse
+import json
+
+def perform_pre_checks():
+ mock_result_path = os.path.join(os.getcwd(), "mock_result")
+ if(os.path.isfile(mock_result_path)):
+ with open(mock_result_path, "r", encoding="utf8") as f:
+ mock_result = f.read()
+ if mock_result[0]:
+ sys.stdout.write(mock_result[2:])
+ sys.exit(int(mock_result[0]))
+
+def enumerate(args):
+ sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))
+
+def getdescriptors(args):
+ xpub = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B"
+
+ sys.stdout.write(json.dumps({
+ "receive": [
+ "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j",
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#r0grqw5x",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#x30uthjs"
+ ],
+ "internal": [
+ "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2",
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#kwx4c3pe",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#h92akzzg"
+ ]
+ }))
+
+
+def displayaddress(args):
+ # Several descriptor formats are acceptable, so allowing for potential
+ # changes to InferDescriptor:
+ if args.fingerprint != "00000001":
+ return sys.stdout.write(json.dumps({"error": "Unexpected fingerprint", "fingerprint": args.fingerprint}))
+
+ expected_desc = [
+ "wpkh([00000001/84'/1'/0'/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#0yneg42r"
+ ]
+ if args.desc not in expected_desc:
+ return sys.stdout.write(json.dumps({"error": "Unexpected descriptor", "desc": args.desc}))
+
+ return sys.stdout.write(json.dumps({"address": "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g"}))
+
+def signtx(args):
+ if args.fingerprint != "00000001":
+ return sys.stdout.write(json.dumps({"error": "Unexpected fingerprint", "fingerprint": args.fingerprint}))
+
+ with open(os.path.join(os.getcwd(), "mock_psbt"), "r", encoding="utf8") as f:
+ mock_psbt = f.read()
+
+ if args.fingerprint == "00000001" :
+ sys.stdout.write(json.dumps({
+ "psbt": mock_psbt,
+ "complete": True
+ }))
+ else:
+ sys.stdout.write(json.dumps({"psbt": args.psbt}))
+
+parser = argparse.ArgumentParser(prog='./signer.py', description='External signer mock')
+parser.add_argument('--fingerprint')
+parser.add_argument('--chain', default='main')
+parser.add_argument('--stdin', action='store_true')
+
+subparsers = parser.add_subparsers(description='Commands', dest='command')
+subparsers.required = True
+
+parser_enumerate = subparsers.add_parser('enumerate', help='list available signers')
+parser_enumerate.set_defaults(func=enumerate)
+
+parser_getdescriptors = subparsers.add_parser('getdescriptors')
+parser_getdescriptors.set_defaults(func=getdescriptors)
+parser_getdescriptors.add_argument('--account', metavar='account')
+
+parser_displayaddress = subparsers.add_parser('displayaddress', help='display address on signer')
+parser_displayaddress.add_argument('--desc', metavar='desc')
+parser_displayaddress.set_defaults(func=displayaddress)
+
+parser_signtx = subparsers.add_parser('signtx')
+parser_signtx.add_argument('psbt', metavar='psbt')
+
+parser_signtx.set_defaults(func=signtx)
+
+if not sys.stdin.isatty():
+ buffer = sys.stdin.read()
+ if buffer and buffer.rstrip() != "":
+ sys.argv.extend(buffer.rstrip().split(" "))
+
+args = parser.parse_args()
+
+perform_pre_checks()
+
+args.func(args)
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index cca7390ae3..8783c244c3 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -154,7 +154,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333)))
orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
- with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
+ with node.assert_debug_log(['orphanage overflow, removed 1 tx']):
node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
rejected_parent = CTransaction()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 84ca1b99c2..e090030205 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -23,6 +23,7 @@ import http.client
import os
import subprocess
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.blocktools import (
create_block,
create_coinbase,
@@ -71,11 +72,10 @@ class BlockchainTest(BitcoinTestFramework):
def mine_chain(self):
self.log.info('Create some old blocks')
- address = self.nodes[0].get_deterministic_priv_key().address
for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
# ten-minute steps from genesis block time
self.nodes[0].setmocktime(t)
- self.nodes[0].generatetoaddress(1, address)
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
def _test_getblockchaininfo(self):
@@ -227,7 +227,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['transactions'], 200)
assert_equal(res['height'], 200)
assert_equal(res['txouts'], 200)
- assert_equal(res['bogosize'], 15000),
+ assert_equal(res['bogosize'], 16800),
assert_equal(res['bestblock'], node.getblockhash(200))
size = res['disk_size']
assert size > 6400
@@ -316,6 +316,9 @@ class BlockchainTest(BitcoinTestFramework):
header.calc_sha256()
assert_equal(header.hash, besthash)
+ assert 'previousblockhash' not in node.getblockheader(node.getblockhash(0))
+ assert 'nextblockhash' not in node.getblockheader(node.getbestblockhash())
+
def _test_getdifficulty(self):
difficulty = self.nodes[0].getdifficulty()
# 1 hash in 2 should be valid, so difficulty should be 1/2**31
@@ -329,12 +332,12 @@ class BlockchainTest(BitcoinTestFramework):
def _test_stopatheight(self):
assert_equal(self.nodes[0].getblockcount(), 200)
- self.nodes[0].generatetoaddress(6, self.nodes[0].get_deterministic_priv_key().address)
+ self.nodes[0].generatetoaddress(6, ADDRESS_BCRT1_P2WSH_OP_TRUE)
assert_equal(self.nodes[0].getblockcount(), 206)
self.log.debug('Node should not stop at this height')
assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
try:
- self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
except (ConnectionError, http.client.BadStatusLine):
pass # The node already shut down before response
self.log.debug('Node should stop at this height...')
@@ -384,8 +387,7 @@ class BlockchainTest(BitcoinTestFramework):
node = self.nodes[0]
miniwallet = MiniWallet(node)
- miniwallet.generate(5)
- node.generate(100)
+ miniwallet.scan_blocks(num=5)
fee_per_byte = Decimal('0.00000010')
fee_per_kb = 1000 * fee_per_byte
@@ -420,6 +422,9 @@ class BlockchainTest(BitcoinTestFramework):
# Restore chain state
move_block_file('rev_wrong', 'rev00000.dat')
+ assert 'previousblockhash' not in node.getblock(node.getblockhash(0))
+ assert 'nextblockhash' not in node.getblock(node.getbestblockhash())
+
if __name__ == '__main__':
BlockchainTest().main()
diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py
index 044dbd35bf..a99e50f29f 100755
--- a/test/functional/rpc_getblockfilter.py
+++ b/test/functional/rpc_getblockfilter.py
@@ -54,5 +54,11 @@ class GetBlockFilterTest(BitcoinTestFramework):
genesis_hash = self.nodes[0].getblockhash(0)
assert_raises_rpc_error(-5, "Unknown filtertype", self.nodes[0].getblockfilter, genesis_hash, "unknown")
+ # Test getblockfilter fails on node without compact block filter index
+ self.restart_node(0, extra_args=["-blockfilterindex=0"])
+ for filter_type in FILTER_TYPES:
+ assert_raises_rpc_error(-1, "Index is not enabled for filtertype {}".format(filter_type),
+ self.nodes[0].getblockfilter, genesis_hash, filter_type)
+
if __name__ == '__main__':
GetBlockFilterTest().main()
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index 1eefd109f8..de21f43747 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -105,10 +105,13 @@ class HelpRpcTest(BitcoinTestFramework):
if self.is_wallet_compiled():
components.append('Wallet')
+ if self.is_external_signer_compiled():
+ components.append('Signer')
+
if self.is_zmq_compiled():
components.append('Zmq')
- assert_equal(titles, components)
+ assert_equal(titles, sorted(components))
def dump_help(self):
dump_dir = os.path.join(self.options.tmpdir, 'rpc_help_dump')
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 2d41963beb..9adb32c3c5 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -105,7 +105,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][1]['connection_type'], 'inbound')
# Check dynamically generated networks list in getpeerinfo help output.
- assert "(ipv4, ipv6, onion, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
+ assert "(ipv4, ipv6, onion, i2p, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
def test_getnettotals(self):
self.log.info("Test getnettotals")
@@ -156,7 +156,7 @@ class NetTest(BitcoinTestFramework):
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
# Check dynamically generated networks list in getnetworkinfo help output.
- assert "(ipv4, ipv6, onion)" in self.nodes[0].help("getnetworkinfo")
+ assert "(ipv4, ipv6, onion, i2p)" in self.nodes[0].help("getnetworkinfo")
def test_getaddednodeinfo(self):
self.log.info("Test getaddednodeinfo")
diff --git a/test/functional/rpc_signmessage.py b/test/functional/rpc_signmessage.py
index 0cb3ce4215..1c71732a61 100755
--- a/test/functional/rpc_signmessage.py
+++ b/test/functional/rpc_signmessage.py
@@ -5,7 +5,10 @@
"""Test RPC commands for signing and verifying messages."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
class SignMessagesTest(BitcoinTestFramework):
def set_test_params(self):
@@ -38,5 +41,22 @@ class SignMessagesTest(BitcoinTestFramework):
assert not self.nodes[0].verifymessage(other_address, signature, message)
assert not self.nodes[0].verifymessage(address, other_signature, message)
+ self.log.info('test parameter validity and error codes')
+ # signmessage(withprivkey) have two required parameters
+ for num_params in [0, 1, 3, 4, 5]:
+ param_list = ["dummy"]*num_params
+ assert_raises_rpc_error(-1, "signmessagewithprivkey", self.nodes[0].signmessagewithprivkey, *param_list)
+ assert_raises_rpc_error(-1, "signmessage", self.nodes[0].signmessage, *param_list)
+ # verifymessage has three required parameters
+ for num_params in [0, 1, 2, 4, 5]:
+ param_list = ["dummy"]*num_params
+ assert_raises_rpc_error(-1, "verifymessage", self.nodes[0].verifymessage, *param_list)
+ # invalid key or address provided
+ assert_raises_rpc_error(-5, "Invalid private key", self.nodes[0].signmessagewithprivkey, "invalid_key", message)
+ assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].signmessage, "invalid_addr", message)
+ assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].verifymessage, "invalid_addr", signature, message)
+ # malformed signature provided
+ assert_raises_rpc_error(-3, "Malformed base64 encoding", self.nodes[0].verifymessage, self.nodes[0].getnewaddress(), "invalid_sig", message)
+
if __name__ == '__main__':
SignMessagesTest().main()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 70a9798449..02eb10b5a4 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -19,6 +19,7 @@ import tempfile
import time
from typing import List
+from .address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from .authproxy import JSONRPCException
from . import coverage
from .p2p import NetworkThread
@@ -732,16 +733,17 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Set a time in the past, so that blocks don't end up in the future
cache_node.setmocktime(cache_node.getblockheader(cache_node.getbestblockhash())['time'])
- # Create a 199-block-long chain; each of the 4 first nodes
+ # Create a 199-block-long chain; each of the 3 first nodes
# gets 25 mature blocks and 25 immature.
- # The 4th node gets only 24 immature blocks so that the very last
+ # The 4th address gets 25 mature and only 24 immature blocks so that the very last
# block in the cache does not age too much (have an old tip age).
# This is needed so that we are out of IBD when the test starts,
# see the tip age check in IsInitialBlockDownload().
+ gen_addresses = [k.address for k in TestNode.PRIV_KEYS] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
for i in range(8):
cache_node.generatetoaddress(
nblocks=25 if i != 7 else 24,
- address=TestNode.PRIV_KEYS[i % 4].address,
+ address=gen_addresses[i % 4],
)
assert_equal(cache_node.getblockchaininfo()["blocks"], 199)
@@ -827,10 +829,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.previous_releases_path))
return self.options.prev_releases
+ def skip_if_no_external_signer(self):
+ """Skip the running test if external signer support has not been compiled."""
+ if not self.is_external_signer_compiled():
+ raise SkipTest("external signer support has not been compiled.")
+
def is_cli_compiled(self):
"""Checks whether bitcoin-cli was compiled."""
return self.config["components"].getboolean("ENABLE_CLI")
+ def is_external_signer_compiled(self):
+ """Checks whether external signer support was compiled."""
+ return self.config["components"].getboolean("ENABLE_EXTERNAL_SIGNER")
+
def is_wallet_compiled(self):
"""Checks whether the wallet module was compiled."""
return self.config["components"].getboolean("ENABLE_WALLET")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index b820c36e6e..ce9c1bc024 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -678,10 +678,10 @@ class RPCOverloadWrapper():
def __getattr__(self, name):
return getattr(self.rpc, name)
- def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None):
+ def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None, external_signer=None):
if descriptors is None:
descriptors = self.descriptors
- return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup)
+ return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup, external_signer)
def importprivkey(self, privkey, label=None, rescan=None):
wallet_info = self.getwalletinfo()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 123c48852c..d335d4ea79 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -368,6 +368,7 @@ def write_config(config_path, *, n, chain, extra_config=""):
f.write("keypool=1\n")
f.write("discover=0\n")
f.write("dnsseed=0\n")
+ f.write("fixedseeds=0\n")
f.write("listenonion=0\n")
f.write("printtoconsole=0\n")
f.write("upnp=0\n")
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index edd7792608..38fbf3c1a6 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -32,6 +32,15 @@ class MiniWallet:
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ def scan_blocks(self, *, start=1, num):
+ """Scan the blocks for self._address outputs and add them to self._utxos"""
+ for i in range(start, start + num):
+ block = self._test_node.getblock(blockhash=self._test_node.getblockhash(i), verbosity=2)
+ for tx in block['tx']:
+ for out in tx['vout']:
+ if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
+ self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
+
def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
blocks = self._test_node.generatetoaddress(num_blocks, self._address)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index d742ef4eee..79ad2cf161 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -111,6 +111,7 @@ BASE_SCRIPTS = [
'wallet_listtransactions.py --legacy-wallet',
'wallet_listtransactions.py --descriptors',
'feature_taproot.py',
+ 'wallet_signer.py --descriptors',
# vv Tests less than 60s vv
'p2p_sendheaders.py',
'wallet_importmulti.py --legacy-wallet',
diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
index 9f8c341bc7..8d02949ff4 100755
--- a/test/functional/wallet_listdescriptors.py
+++ b/test/functional/wallet_listdescriptors.py
@@ -50,6 +50,22 @@ class ListDescriptorsTest(BitcoinTestFramework):
assert item['range'] == [0, 0]
assert item['timestamp'] is not None
+ self.log.info('Test descriptors with hardened derivations are listed in importable form.')
+ xprv = 'tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg'
+ xpub_acc = 'tpubDCMVLhErorrAGfApiJSJzEKwqeaf2z3NrkVMxgYQjZLzMjXMBeRw2muGNYbvaekAE8rUFLftyEar4LdrG2wXyyTJQZ26zptmeTEjPTaATts'
+ hardened_path = '/84\'/1\'/0\''
+ wallet = node.get_wallet_rpc('w2')
+ wallet.importdescriptors([{
+ 'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'),
+ 'timestamp': 1296688602,
+ }])
+ expected = {'desc': descsum_create('wpkh([80002067' + hardened_path + ']' + xpub_acc + '/0/*)'),
+ 'timestamp': 1296688602,
+ 'active': False,
+ 'range': [0, 0],
+ 'next': 0}
+ assert_equal([expected], wallet.listdescriptors())
+
self.log.info('Test non-active non-range combo descriptor')
node.createwallet(wallet_name='w4', blank=True, descriptors=True)
wallet = node.get_wallet_rpc('w4')
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
new file mode 100755
index 0000000000..9dd080dca9
--- /dev/null
+++ b/test/functional/wallet_signer.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-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 external signer.
+
+Verify that a bitcoind node can use an external signer command
+"""
+import os
+import platform
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+
+class SignerTest(BitcoinTestFramework):
+ def mock_signer_path(self):
+ path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py')
+ if platform.system() == "Windows":
+ return "py " + path
+ else:
+ return path
+
+ def set_test_params(self):
+ self.num_nodes = 4
+
+ self.extra_args = [
+ [],
+ [f"-signer={self.mock_signer_path()}", '-keypool=10'],
+ [f"-signer={self.mock_signer_path()}", '-keypool=10'],
+ ["-signer=fake.py"],
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_external_signer()
+
+ def set_mock_result(self, node, res):
+ with open(os.path.join(node.cwd, "mock_result"), "w", encoding="utf8") as f:
+ f.write(res)
+
+ def clear_mock_result(self, node):
+ os.remove(os.path.join(node.cwd, "mock_result"))
+
+ def run_test(self):
+ self.log.debug(f"-signer={self.mock_signer_path()}")
+
+ assert_raises_rpc_error(-4, 'Error: restart bitcoind with -signer=<cmd>',
+ self.nodes[0].enumeratesigners
+ )
+
+ # Handle script missing:
+ assert_raises_rpc_error(-1, 'execve failed: No such file or directory',
+ self.nodes[3].enumeratesigners
+ )
+
+ # Handle error thrown by script
+ self.set_mock_result(self.nodes[1], "2")
+ assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
+ self.nodes[1].enumeratesigners
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "error": "fingerprint not found"}]')
+ assert_raises_rpc_error(-4, 'fingerprint not found',
+ self.nodes[1].enumeratesigners
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ # Create new wallets for an external signer.
+ # disable_private_keys and descriptors must be true:
+ assert_raises_rpc_error(-4, "Private keys must be disabled when using an external signer", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=False, descriptors=True, external_signer=True)
+ if self.is_bdb_compiled():
+ assert_raises_rpc_error(-4, "Descriptor support must be enabled when using an external signer", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=True, descriptors=False, external_signer=True)
+ else:
+ assert_raises_rpc_error(-4, "Compiled without bdb support (required for legacy wallets)", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=True, descriptors=False, external_signer=True)
+
+ self.nodes[1].createwallet(wallet_name='hww', disable_private_keys=True, descriptors=True, external_signer=True)
+ hww = self.nodes[1].get_wallet_rpc('hww')
+
+ result = hww.enumeratesigners()
+ assert_equal(len(result['signers']), 2)
+ assert_equal(result['signers'][0]["fingerprint"], "00000001")
+ assert_equal(result['signers'][0]["name"], "trezor_t")
+
+ # Flag can't be set afterwards (could be added later for non-blank descriptor based watch-only wallets)
+ self.nodes[1].createwallet(wallet_name='not_hww', disable_private_keys=True, descriptors=True, external_signer=False)
+ not_hww = self.nodes[1].get_wallet_rpc('not_hww')
+ assert_raises_rpc_error(-8, "Wallet flag is immutable: external_signer", not_hww.setwalletflag, "external_signer", True)
+
+ # assert_raises_rpc_error(-4, "Multiple signers found, please specify which to use", wallet_name='not_hww', disable_private_keys=True, descriptors=True, external_signer=True)
+
+ # TODO: Handle error thrown by script
+ # self.set_mock_result(self.nodes[1], "2")
+ # assert_raises_rpc_error(-1, 'Unable to parse JSON',
+ # self.nodes[1].createwallet, wallet_name='not_hww2', disable_private_keys=True, descriptors=True, external_signer=False
+ # )
+ # self.clear_mock_result(self.nodes[1])
+
+ assert_equal(hww.getwalletinfo()["keypoolsize"], 30)
+
+ address1 = hww.getnewaddress(address_type="bech32")
+ assert_equal(address1, "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g")
+ address_info = hww.getaddressinfo(address1)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/84'/1'/0'/0/0")
+
+ address2 = hww.getnewaddress(address_type="p2sh-segwit")
+ assert_equal(address2, "2N2gQKzjUe47gM8p1JZxaAkTcoHPXV6YyVp")
+ address_info = hww.getaddressinfo(address2)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/49'/1'/0'/0/0")
+
+ address3 = hww.getnewaddress(address_type="legacy")
+ assert_equal(address3, "n1LKejAadN6hg2FrBXoU1KrwX4uK16mco9")
+ address_info = hww.getaddressinfo(address3)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/44'/1'/0'/0/0")
+
+ self.log.info('Test signerdisplayaddress')
+ result = hww.signerdisplayaddress(address1)
+ assert_equal(result, {"address": address1})
+
+ # Handle error thrown by script
+ self.set_mock_result(self.nodes[1], "2")
+ assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
+ hww.signerdisplayaddress, address1
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ self.log.info('Prepare mock PSBT')
+ self.nodes[0].sendtoaddress(address1, 1)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # Load private key into wallet to generate a signed PSBT for the mock
+ self.nodes[1].createwallet(wallet_name="mock", disable_private_keys=False, blank=True, descriptors=True)
+ mock_wallet = self.nodes[1].get_wallet_rpc("mock")
+ assert mock_wallet.getwalletinfo()['private_keys_enabled']
+
+ result = mock_wallet.importdescriptors([{
+ "desc": "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0",
+ "timestamp": 0,
+ "range": [0,1],
+ "internal": False,
+ "active": True
+ },
+ {
+ "desc": "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh",
+ "timestamp": 0,
+ "range": [0, 0],
+ "internal": True,
+ "active": True
+ }])
+ assert_equal(result[0], {'success': True})
+ assert_equal(result[1], {'success': True})
+ assert_equal(mock_wallet.getwalletinfo()["txcount"], 1)
+ dest = self.nodes[0].getnewaddress(address_type='bech32')
+ mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {}, True)['psbt']
+ mock_psbt_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt, sign=True, sighashtype="ALL", bip32derivs=True)
+ mock_psbt_final = mock_wallet.finalizepsbt(mock_psbt_signed["psbt"])
+ mock_tx = mock_psbt_final["hex"]
+ assert(mock_wallet.testmempoolaccept([mock_tx])[0]["allowed"])
+
+ # # Create a new wallet and populate with specific public keys, in order
+ # # to work with the mock signed PSBT.
+ # self.nodes[1].createwallet(wallet_name="hww4", disable_private_keys=True, descriptors=True, external_signer=True)
+ # hww4 = self.nodes[1].get_wallet_rpc("hww4")
+ #
+ # descriptors = [{
+ # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/*)#x30uthjs",
+ # "timestamp": "now",
+ # "range": [0, 1],
+ # "internal": False,
+ # "watchonly": True,
+ # "active": True
+ # },
+ # {
+ # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)#h92akzzg",
+ # "timestamp": "now",
+ # "range": [0, 0],
+ # "internal": True,
+ # "watchonly": True,
+ # "active": True
+ # }]
+
+ # result = hww4.importdescriptors(descriptors)
+ # assert_equal(result[0], {'success': True})
+ # assert_equal(result[1], {'success': True})
+ assert_equal(hww.getwalletinfo()["txcount"], 1)
+
+ assert(hww.testmempoolaccept([mock_tx])[0]["allowed"])
+
+ with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f:
+ f.write(mock_psbt_signed["psbt"])
+
+ self.log.info('Test send using hww1')
+
+ res = hww.send(outputs={dest:0.5},options={"add_to_wallet": False})
+ assert(res["complete"])
+ assert_equal(res["hex"], mock_tx)
+
+ # # Handle error thrown by script
+ # self.set_mock_result(self.nodes[4], "2")
+ # assert_raises_rpc_error(-1, 'Unable to parse JSON',
+ # hww4.signerprocesspsbt, psbt_orig, "00000001"
+ # )
+ # self.clear_mock_result(self.nodes[4])
+
+if __name__ == '__main__':
+ SignerTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 893a2d9617..6fc1d13c53 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -11,9 +11,10 @@ from test_framework.util import (
)
from test_framework.messages import CTransaction, COIN
+
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 3
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -38,9 +39,8 @@ class TxnMallTest(BitcoinTestFramework):
# All nodes should start with 1,250 BTC:
starting_balance = 1250
- for i in range(4):
+ for i in range(3):
assert_equal(self.nodes[i].getbalance(), starting_balance)
- self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress!
self.nodes[0].settxfee(.001)
@@ -139,5 +139,6 @@ class TxnMallTest(BitcoinTestFramework):
expected -= 50
assert_equal(self.nodes[0].getbalance(), expected)
+
if __name__ == '__main__':
TxnMallTest().main()
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index c7f7a8546a..0cb7328948 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -11,9 +11,10 @@ from test_framework.util import (
find_output,
)
+
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 3
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -39,9 +40,8 @@ class TxnMallTest(BitcoinTestFramework):
for n in self.nodes:
assert n.getblockchaininfo()["initialblockdownload"] == False
- for i in range(4):
+ for i in range(3):
assert_equal(self.nodes[i].getbalance(), starting_balance)
- self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
# Assign coins to foo and bar addresses:
node0_address_foo = self.nodes[0].getnewaddress()
@@ -136,5 +136,6 @@ class TxnMallTest(BitcoinTestFramework):
# Node1's balance should be its initial balance (1250 for 25 block rewards) plus the doublespend:
assert_equal(self.nodes[1].getbalance(), 1250 + 1240)
+
if __name__ == '__main__':
TxnMallTest().main()