Skip to content

Commit 1194cf9

Browse files
committed
Fix wallet_send.py wallet setup to work with descriptors
Fixes the wallet setup so this test works with descriptor wallets. Also enabled explicit descriptor and legacy wallet testing in the test runner.
1 parent fbaea7b commit 1194cf9

File tree

2 files changed

+76
-28
lines changed

2 files changed

+76
-28
lines changed

test/functional/test_runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@
252252
'rpc_estimatefee.py',
253253
'rpc_getblockstats.py',
254254
'wallet_create_tx.py --legacy-wallet',
255-
'wallet_send.py',
255+
'wallet_send.py --legacy-wallet',
256+
'wallet_send.py --descriptors',
256257
'wallet_create_tx.py --descriptors',
257258
'p2p_fingerprint.py',
258259
'feature_uacomment.py',

test/functional/wallet_send.py

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from itertools import product
99

1010
from test_framework.authproxy import JSONRPCException
11+
from test_framework.descriptors import descsum_create
1112
from test_framework.test_framework import BitcoinTestFramework
1213
from test_framework.util import (
1314
assert_equal,
@@ -168,49 +169,91 @@ def run_test(self):
168169
self.nodes[1].createwallet(wallet_name="w1")
169170
w1 = self.nodes[1].get_wallet_rpc("w1")
170171
# w2 contains the private keys for w3
171-
self.nodes[1].createwallet(wallet_name="w2")
172+
self.nodes[1].createwallet(wallet_name="w2", blank=True)
172173
w2 = self.nodes[1].get_wallet_rpc("w2")
174+
xpriv = "tprv8ZgxMBicQKsPfHCsTwkiM1KT56RXbGGTqvc2hgqzycpwbHqqpcajQeMRZoBD35kW4RtyCemu6j34Ku5DEspmgjKdt2qe4SvRch5Kk8B8A2v"
175+
xpub = "tpubD6NzVbkrYhZ4YkEfMbRJkQyZe7wTkbTNRECozCtJPtdLRn6cT1QKb8yHjwAPcAr26eHBFYs5iLiFFnCbwPRsncCKUKCfubHDMGKzMVcN1Jg"
176+
if self.options.descriptors:
177+
w2.importdescriptors([{
178+
"desc": descsum_create("wpkh(" + xpriv + "/0/0/*)"),
179+
"timestamp": "now",
180+
"range": [0, 100],
181+
"active": True
182+
},{
183+
"desc": descsum_create("wpkh(" + xpriv + "/0/1/*)"),
184+
"timestamp": "now",
185+
"range": [0, 100],
186+
"active": True,
187+
"internal": True
188+
}])
189+
else:
190+
w2.sethdseed(True)
191+
173192
# w3 is a watch-only wallet, based on w2
174193
self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
175194
w3 = self.nodes[1].get_wallet_rpc("w3")
176-
for _ in range(3):
177-
a2_receive = w2.getnewaddress()
178-
a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
179-
res = w3.importmulti([{
180-
"desc": w2.getaddressinfo(a2_receive)["desc"],
195+
if self.options.descriptors:
196+
# Match the privkeys in w2 for descriptors
197+
res = w3.importdescriptors([{
198+
"desc": descsum_create("wpkh(" + xpub + "/0/0/*)"),
181199
"timestamp": "now",
200+
"range": [0, 100],
182201
"keypool": True,
202+
"active": True,
183203
"watchonly": True
184204
},{
185-
"desc": w2.getaddressinfo(a2_change)["desc"],
205+
"desc": descsum_create("wpkh(" + xpub + "/0/1/*)"),
186206
"timestamp": "now",
207+
"range": [0, 100],
187208
"keypool": True,
209+
"active": True,
188210
"internal": True,
189211
"watchonly": True
190212
}])
191213
assert_equal(res, [{"success": True}, {"success": True}])
192214

193-
w0.sendtoaddress(a2_receive, 10) # fund w3
194-
self.nodes[0].generate(1)
195-
self.sync_blocks()
196-
197-
# w4 has private keys enabled, but only contains watch-only keys (from w2)
198-
self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
199-
w4 = self.nodes[1].get_wallet_rpc("w4")
200215
for _ in range(3):
201216
a2_receive = w2.getnewaddress()
202-
res = w4.importmulti([{
203-
"desc": w2.getaddressinfo(a2_receive)["desc"],
204-
"timestamp": "now",
205-
"keypool": False,
206-
"watchonly": True
207-
}])
208-
assert_equal(res, [{"success": True}])
217+
if not self.options.descriptors:
218+
# Because legacy wallets use exclusively hardened derivation, we can't do a ranged import like we do for descriptors
219+
a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
220+
res = w3.importmulti([{
221+
"desc": w2.getaddressinfo(a2_receive)["desc"],
222+
"timestamp": "now",
223+
"keypool": True,
224+
"watchonly": True
225+
},{
226+
"desc": w2.getaddressinfo(a2_change)["desc"],
227+
"timestamp": "now",
228+
"keypool": True,
229+
"internal": True,
230+
"watchonly": True
231+
}])
232+
assert_equal(res, [{"success": True}, {"success": True}])
209233

210-
w0.sendtoaddress(a2_receive, 10) # fund w4
234+
w0.sendtoaddress(a2_receive, 10) # fund w3
211235
self.nodes[0].generate(1)
212236
self.sync_blocks()
213237

238+
if not self.options.descriptors:
239+
# w4 has private keys enabled, but only contains watch-only keys (from w2)
240+
# This is legacy wallet behavior only as descriptor wallets don't allow watchonly and non-watchonly things in the same wallet.
241+
self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
242+
w4 = self.nodes[1].get_wallet_rpc("w4")
243+
for _ in range(3):
244+
a2_receive = w2.getnewaddress()
245+
res = w4.importmulti([{
246+
"desc": w2.getaddressinfo(a2_receive)["desc"],
247+
"timestamp": "now",
248+
"keypool": False,
249+
"watchonly": True
250+
}])
251+
assert_equal(res, [{"success": True}])
252+
253+
w0.sendtoaddress(a2_receive, 10) # fund w4
254+
self.nodes[0].generate(1)
255+
self.sync_blocks()
256+
214257
self.log.info("Send to address...")
215258
self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
216259
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True)
@@ -241,11 +284,15 @@ def run_test(self):
241284
res = w2.walletprocesspsbt(res["psbt"])
242285
assert res["complete"]
243286

244-
self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
245-
self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
246-
res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
247-
res = w2.walletprocesspsbt(res["psbt"])
248-
assert res["complete"]
287+
if not self.options.descriptors:
288+
# Descriptor wallets do not allow mixed watch-only and non-watch-only things in the same wallet.
289+
# This is specifically testing that w4 ignores its own private keys and creates a psbt with send
290+
# which is not something that needs to be tested in descriptor wallets.
291+
self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
292+
self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
293+
res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
294+
res = w2.walletprocesspsbt(res["psbt"])
295+
assert res["complete"]
249296

250297
self.log.info("Create OP_RETURN...")
251298
self.test_send(from_wallet=w0, to_wallet=w1, amount=1)

0 commit comments

Comments
 (0)