From 9dfc61d95f0082672a9b90528386e6bcd7014a78 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 13 Jun 2025 09:40:23 +0200 Subject: [PATCH] test: detect no external signer connected --- test/functional/mocks/no_signer.py | 29 +++++++++++++++++++++++++++++ test/functional/wallet_signer.py | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100755 test/functional/mocks/no_signer.py diff --git a/test/functional/mocks/no_signer.py b/test/functional/mocks/no_signer.py new file mode 100755 index 00000000000..04318dac793 --- /dev/null +++ b/test/functional/mocks/no_signer.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# Copyright (c) 2025-present 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 argparse +import json +import sys + +def enumerate(args): + sys.stdout.write(json.dumps([])) + +parser = argparse.ArgumentParser(prog='./no_signer.py', description='No external signer connected mock') + +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) + + +if not sys.stdin.isatty(): + buffer = sys.stdin.read() + if buffer and buffer.rstrip() != "": + sys.argv.extend(buffer.rstrip().split(" ")) + +args = parser.parse_args() + +args.func(args) diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py index c9ac876eb71..208e4c07752 100755 --- a/test/functional/wallet_signer.py +++ b/test/functional/wallet_signer.py @@ -23,6 +23,10 @@ class WalletSignerTest(BitcoinTestFramework): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py') return sys.executable + " " + path + def mock_no_connected_signer_path(self): + path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'no_signer.py') + return sys.executable + " " + path + def mock_invalid_signer_path(self): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'invalid_signer.py') return sys.executable + " " + path @@ -52,6 +56,7 @@ class WalletSignerTest(BitcoinTestFramework): def run_test(self): self.test_valid_signer() + self.test_disconnected_signer() self.restart_node(1, [f"-signer={self.mock_invalid_signer_path()}", "-keypool=10"]) self.test_invalid_signer() self.restart_node(1, [f"-signer={self.mock_multi_signers_path()}", "-keypool=10"]) @@ -234,6 +239,28 @@ class WalletSignerTest(BitcoinTestFramework): # ) # self.clear_mock_result(self.nodes[4]) + def test_disconnected_signer(self): + self.log.info('Test disconnected external signer') + + # First create a wallet with the signer connected + self.nodes[1].createwallet(wallet_name='hww_disconnect', disable_private_keys=True, external_signer=True) + hww = self.nodes[1].get_wallet_rpc('hww_disconnect') + assert_equal(hww.getwalletinfo()["external_signer"], True) + + # Fund wallet + self.nodes[0].sendtoaddress(hww.getnewaddress(address_type="bech32m"), 1) + self.generate(self.nodes[0], 1) + + # Restart node with no signer connected + self.log.debug(f"-signer={self.mock_no_connected_signer_path()}") + self.restart_node(1, [f"-signer={self.mock_no_connected_signer_path()}", "-keypool=10"]) + self.nodes[1].loadwallet('hww_disconnect') + hww = self.nodes[1].get_wallet_rpc('hww_disconnect') + + # Try to spend + dest = hww.getrawchangeaddress() + assert_raises_rpc_error(-25, "External signer not found", hww.send, outputs=[{dest:0.5}]) + def test_invalid_signer(self): self.log.debug(f"-signer={self.mock_invalid_signer_path()}") self.log.info('Test invalid external signer')