Skip to content

Commit 292b1a3

Browse files
GetExternalSigner(): fail if multiple signers are found
If there are multiple external signers, `GetExternalSigner()` will just pick the first one in the list. If the user has two or more hardware wallets connected at the same time, he might not notice this. This PR adds a check and fails with suitable message.
1 parent 8c61374 commit 292b1a3

File tree

7 files changed

+54
-7
lines changed

7 files changed

+54
-7
lines changed

src/qt/walletcontroller.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@ void CreateWalletActivity::create()
293293
} catch (const std::runtime_error& e) {
294294
QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
295295
}
296+
if (signers.size() > 1) {
297+
QMessageBox::critical(nullptr, tr("Too many external signers found"), QString::fromStdString("More than one external signer found. Please connect only one at a time."));
298+
signers.clear();
299+
}
296300
m_create_wallet_dialog->setSigners(signers);
297301

298302
m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);

src/wallet/external_signer_scriptpubkeyman.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
4545
std::vector<ExternalSigner> signers;
4646
ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
4747
if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found");
48-
// TODO: add fingerprint argument in case of multiple signers
48+
// TODO: add fingerprint argument instead of failing in case of multiple signers.
49+
if (signers.size() > 1) throw std::runtime_error(std::string(__func__) + ": More than one external signer found. Please connect only one at a time.");
4950
return signers[0];
5051
}
5152

test/functional/mocks/invalid_signer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def perform_pre_checks():
1818
sys.exit(int(mock_result[0]))
1919

2020
def enumerate(args):
21-
sys.stdout.write(json.dumps([{"fingerprint": "b3c19bfc", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))
21+
sys.stdout.write(json.dumps([{"fingerprint": "b3c19bfc", "type": "trezor", "model": "trezor_t"}]))
2222

2323
def getdescriptors(args):
2424
xpub_pkh = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6"
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
import argparse
7+
import json
8+
import sys
9+
10+
def enumerate(args):
11+
sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"},
12+
{"fingerprint": "00000002", "type": "trezor", "model": "trezor_one"}]))
13+
14+
parser = argparse.ArgumentParser(prog='./multi_signers.py', description='External multi-signer mock')
15+
16+
subparsers = parser.add_subparsers(description='Commands', dest='command')
17+
subparsers.required = True
18+
19+
parser_enumerate = subparsers.add_parser('enumerate', help='list available signers')
20+
parser_enumerate.set_defaults(func=enumerate)
21+
22+
23+
if not sys.stdin.isatty():
24+
buffer = sys.stdin.read()
25+
if buffer and buffer.rstrip() != "":
26+
sys.argv.extend(buffer.rstrip().split(" "))
27+
28+
args = parser.parse_args()
29+
30+
args.func(args)

test/functional/mocks/signer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def perform_pre_checks():
1818
sys.exit(int(mock_result[0]))
1919

2020
def enumerate(args):
21-
sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))
21+
sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}]))
2222

2323
def getdescriptors(args):
2424
xpub = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B"

test/functional/rpc_signer.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,7 @@ def run_test(self):
7777
)
7878
self.clear_mock_result(self.nodes[1])
7979

80-
result = self.nodes[1].enumeratesigners()
81-
assert_equal(len(result['signers']), 2)
82-
assert_equal(result['signers'][0]["fingerprint"], "00000001")
83-
assert_equal(result['signers'][0]["name"], "trezor_t")
80+
assert_equal({'fingerprint': '00000001', 'name': 'trezor_t'} in self.nodes[1].enumeratesigners()['signers'], True)
8481

8582
if __name__ == '__main__':
8683
RPCSignerTest().main()

test/functional/wallet_signer.py

+15
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ def mock_invalid_signer_path(self):
3232
else:
3333
return path
3434

35+
def mock_multi_signers_path(self):
36+
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'multi_signers.py')
37+
if platform.system() == "Windows":
38+
return "py " + path
39+
else:
40+
return path
41+
3542
def set_test_params(self):
3643
self.num_nodes = 2
3744
# The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
@@ -58,6 +65,8 @@ def run_test(self):
5865
self.test_valid_signer()
5966
self.restart_node(1, [f"-signer={self.mock_invalid_signer_path()}", "-keypool=10"])
6067
self.test_invalid_signer()
68+
self.restart_node(1, [f"-signer={self.mock_multi_signers_path()}", "-keypool=10"])
69+
self.test_multiple_signers()
6170

6271
def test_valid_signer(self):
6372
self.log.debug(f"-signer={self.mock_signer_path()}")
@@ -212,5 +221,11 @@ def test_invalid_signer(self):
212221
self.log.info('Test invalid external signer')
213222
assert_raises_rpc_error(-1, "Invalid descriptor", self.nodes[1].createwallet, wallet_name='hww_invalid', disable_private_keys=True, descriptors=True, external_signer=True)
214223

224+
def test_multiple_signers(self):
225+
self.log.debug(f"-signer={self.mock_multi_signers_path()}")
226+
self.log.info('Test multiple external signers')
227+
228+
assert_raises_rpc_error(-1, "GetExternalSigner: More than one external signer found", self.nodes[1].createwallet, wallet_name='multi_hww', disable_private_keys=True, descriptors=True, external_signer=True)
229+
215230
if __name__ == '__main__':
216231
WalletSignerTest().main()

0 commit comments

Comments
 (0)