Skip to content

Commit 5493e92

Browse files
committed
Check descriptors returned by external signers
Check that descriptors returned by external signers have been parsed properly when creating a new wallet.
1 parent b4f647f commit 5493e92

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

src/wallet/wallet.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3203,8 +3203,11 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
32033203
for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) {
32043204
std::string desc_str = desc_val.getValStr();
32053205
FlatSigningProvider keys;
3206-
std::string dummy_error;
3207-
std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, dummy_error, false);
3206+
std::string desc_error;
3207+
std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, desc_error, false);
3208+
if (desc == nullptr) {
3209+
throw std::runtime_error(std::string(__func__) + ": Invalid descriptor \"" + desc_str + "\" (" + desc_error + ")");
3210+
}
32083211
if (!desc->GetOutputType()) {
32093212
continue;
32103213
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2018-2021 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 os
7+
import sys
8+
import argparse
9+
import json
10+
11+
def perform_pre_checks():
12+
mock_result_path = os.path.join(os.getcwd(), "mock_result")
13+
if(os.path.isfile(mock_result_path)):
14+
with open(mock_result_path, "r", encoding="utf8") as f:
15+
mock_result = f.read()
16+
if mock_result[0]:
17+
sys.stdout.write(mock_result[2:])
18+
sys.exit(int(mock_result[0]))
19+
20+
def enumerate(args):
21+
sys.stdout.write(json.dumps([{"fingerprint": "b3c19bfc", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))
22+
23+
def getdescriptors(args):
24+
xpub_pkh = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6"
25+
xpub_sh = "xpub6CoNoq3Tg4tGSpom2BSwL42gy864KHo3TXkHxLxBbhvCkgmdVXADQmiHbLZhX3Me1cYhRx7s25Lpm4LnT5zu395ANHsXB2QvT9tqJDAibTN"
26+
xpub_wpkh = "xpub6DUcLgY1DfgDy2RV6q4djwwsLitaoZDumbribqrR8mP78fEtgZa1XEsqT5MWQ7gwLwKsTQPT28XLoVE5A97rDNTwMXjmzPaNijoCApCbWvp"
27+
28+
sys.stdout.write(json.dumps({
29+
"receive": [
30+
"pkh([b3c19bfc/44'/1'/" + args.account + "']" + xpub_pkh + "/0/*)#h26nxtl9",
31+
"sh(wpkh([b3c19bfc/49'/1'/" + args.account + "']" + xpub_sh + "/0/*))#32ry02yp",
32+
"wpkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_wpkh + "/0/*)#jftn8ppv"
33+
],
34+
"internal": [
35+
"pkh([b3c19bfc/44'/1'/" + args.account + "']" + xpub_pkh + "/1/*)#x7ljm70a",
36+
"sh(wpkh([b3c19bfc/49'/1'/" + args.account + "']" + xpub_sh + "/1/*))#ytdjh437",
37+
"wpkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_wpkh + "/1/*)#rawj6535"
38+
]
39+
}))
40+
41+
parser = argparse.ArgumentParser(prog='./invalid_signer.py', description='External invalid signer mock')
42+
parser.add_argument('--fingerprint')
43+
parser.add_argument('--chain', default='main')
44+
parser.add_argument('--stdin', action='store_true')
45+
46+
subparsers = parser.add_subparsers(description='Commands', dest='command')
47+
subparsers.required = True
48+
49+
parser_enumerate = subparsers.add_parser('enumerate', help='list available signers')
50+
parser_enumerate.set_defaults(func=enumerate)
51+
52+
parser_getdescriptors = subparsers.add_parser('getdescriptors')
53+
parser_getdescriptors.set_defaults(func=getdescriptors)
54+
parser_getdescriptors.add_argument('--account', metavar='account')
55+
56+
if not sys.stdin.isatty():
57+
buffer = sys.stdin.read()
58+
if buffer and buffer.rstrip() != "":
59+
sys.argv.extend(buffer.rstrip().split(" "))
60+
61+
args = parser.parse_args()
62+
63+
perform_pre_checks()
64+
65+
args.func(args)

test/functional/wallet_signer.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ def mock_signer_path(self):
2525
else:
2626
return path
2727

28+
def mock_invalid_signer_path(self):
29+
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'invalid_signer.py')
30+
if platform.system() == "Windows":
31+
return "py " + path
32+
else:
33+
return path
34+
2835
def set_test_params(self):
2936
self.num_nodes = 2
3037
# The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
@@ -48,6 +55,11 @@ def clear_mock_result(self, node):
4855
os.remove(os.path.join(node.cwd, "mock_result"))
4956

5057
def run_test(self):
58+
self.test_valid_signer()
59+
self.restart_node(1, [f"-signer={self.mock_invalid_signer_path()}", "-keypool=10"])
60+
self.test_invalid_signer()
61+
62+
def test_valid_signer(self):
5163
self.log.debug(f"-signer={self.mock_signer_path()}")
5264

5365
# Create new wallets for an external signer.
@@ -187,5 +199,10 @@ def run_test(self):
187199
# )
188200
# self.clear_mock_result(self.nodes[4])
189201

202+
def test_invalid_signer(self):
203+
self.log.debug(f"-signer={self.mock_invalid_signer_path()}")
204+
self.log.info('Test invalid external signer')
205+
assert_raises_rpc_error(-1, "Invalid descriptor", self.nodes[1].createwallet, wallet_name='hww_invalid', disable_private_keys=True, descriptors=True, external_signer=True)
206+
190207
if __name__ == '__main__':
191208
WalletSignerTest().main()

0 commit comments

Comments
 (0)