wallet_send.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2020-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test the send RPC command."""
6
7 from decimal import Decimal, getcontext
8 from itertools import product
9
10 from test_framework.authproxy import JSONRPCException
11 from test_framework.descriptors import descsum_create
12 from test_framework.test_framework import LimenkaTestFramework
13 from test_framework.util import (
14 assert_equal,
15 assert_fee_amount,
16 assert_greater_than,
17 assert_greater_than_or_equal,
18 assert_raises_rpc_error,
19 count_bytes,
20 )
21 from test_framework.wallet_util import (
22 calculate_input_weight,
23 generate_keypair,
24 )
25
26
27 class WalletSendTest(LimenkaTestFramework):
28 def add_options(self, parser):
29 self.add_wallet_options(parser)
30
31 def set_test_params(self):
32 self.num_nodes = 2
33 # whitelist peers to speed up tx relay / mempool sync
34 self.noban_tx_relay = True
35 self.extra_args = [
36 ["-walletrbf=1"],
37 ["-walletrbf=1"]
38 ]
39 getcontext().prec = 8 # Satoshi precision for Decimal
40
41 def skip_test_if_missing_module(self):
42 self.skip_if_no_wallet()
43
44 def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
45 arg_conf_target=None, arg_estimate_mode=None, arg_fee_rate=None,
46 conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
47 inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
48 include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
49 expect_error=None, solving_data=None, minconf=None):
50 assert (amount is None) != (data is None)
51
52 from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
53 if include_unsafe:
54 from_balance_before += from_wallet.getbalances()["mine"]["untrusted_pending"]
55
56 if to_wallet is None:
57 assert amount is None
58 else:
59 to_untrusted_pending_before = to_wallet.getbalances()["mine"]["untrusted_pending"]
60
61 if amount:
62 dest = to_wallet.getnewaddress()
63 outputs = {dest: amount}
64 else:
65 outputs = {"data": data}
66
67 # Construct options dictionary
68 options = {}
69 if add_to_wallet is not None:
70 options["add_to_wallet"] = add_to_wallet
71 else:
72 if psbt:
73 add_to_wallet = False
74 else:
75 add_to_wallet = from_wallet.getwalletinfo()["private_keys_enabled"] # Default value
76 if psbt is not None:
77 options["psbt"] = psbt
78 if conf_target is not None:
79 options["conf_target"] = conf_target
80 if estimate_mode is not None:
81 options["estimate_mode"] = estimate_mode
82 if fee_rate is not None:
83 options["fee_rate"] = fee_rate
84 if inputs is not None:
85 options["inputs"] = inputs
86 if add_inputs is not None:
87 options["add_inputs"] = add_inputs
88 if include_unsafe is not None:
89 options["include_unsafe"] = include_unsafe
90 if change_address is not None:
91 options["change_address"] = change_address
92 if change_position is not None:
93 options["change_position"] = change_position
94 if change_type is not None:
95 options["change_type"] = change_type
96 if include_watching is not None:
97 options["include_watching"] = include_watching
98 if locktime is not None:
99 options["locktime"] = locktime
100 if lock_unspents is not None:
101 options["lock_unspents"] = lock_unspents
102 if replaceable is None:
103 replaceable = True # default
104 else:
105 options["replaceable"] = replaceable
106 if subtract_fee_from_outputs is not None:
107 options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
108 if solving_data is not None:
109 options["solving_data"] = solving_data
110 if minconf is not None:
111 options["minconf"] = minconf
112
113 if len(options.keys()) == 0:
114 options = None
115
116 if expect_error is None:
117 res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, fee_rate=arg_fee_rate, options=options)
118 else:
119 try:
120 assert_raises_rpc_error(expect_error[0], expect_error[1], from_wallet.send,
121 outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, fee_rate=arg_fee_rate, options=options)
122 except AssertionError:
123 # Provide debug info if the test fails
124 self.log.error("Unexpected successful result:")
125 self.log.error(arg_conf_target)
126 self.log.error(arg_estimate_mode)
127 self.log.error(arg_fee_rate)
128 self.log.error(options)
129 res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, fee_rate=arg_fee_rate, options=options)
130 self.log.error(res)
131 if "txid" in res and add_to_wallet:
132 self.log.error("Transaction details:")
133 try:
134 tx = from_wallet.gettransaction(res["txid"])
135 self.log.error(tx)
136 self.log.error("testmempoolaccept (transaction may already be in mempool):")
137 self.log.error(from_wallet.testmempoolaccept([tx["hex"]]))
138 except JSONRPCException as exc:
139 self.log.error(exc)
140
141 raise
142
143 return
144
145 if locktime:
146 assert_equal(from_wallet.gettransaction(txid=res["txid"], verbose=True)["decoded"]["locktime"], locktime)
147 return res
148 else:
149 if add_to_wallet:
150 decoded_tx = from_wallet.gettransaction(txid=res["txid"], verbose=True)["decoded"]
151 # the locktime should be within 100 blocks of the
152 # block height
153 assert_greater_than_or_equal(decoded_tx["locktime"], from_wallet.getblockcount() - 100)
154
155 if from_wallet.getwalletinfo()["private_keys_enabled"] and not include_watching:
156 assert_equal(res["complete"], True)
157 assert "txid" in res
158 else:
159 assert_equal(res["complete"], False)
160 assert not "txid" in res
161 assert "psbt" in res
162
163 from_balance = from_wallet.getbalances()["mine"]["trusted"]
164 if include_unsafe:
165 from_balance += from_wallet.getbalances()["mine"]["untrusted_pending"]
166
167 if add_to_wallet and not include_watching:
168 # Ensure transaction exists in the wallet:
169 tx = from_wallet.gettransaction(res["txid"])
170 assert tx
171 assert_equal(tx["bip125-replaceable"], "yes" if replaceable else "no")
172 # Ensure transaction exists in the mempool:
173 tx = from_wallet.getrawtransaction(res["txid"], True)
174 assert tx
175 if amount:
176 if subtract_fee_from_outputs:
177 assert_equal(from_balance_before - from_balance, amount)
178 else:
179 assert_greater_than(from_balance_before - from_balance, amount)
180 else:
181 assert next((out for out in tx["vout"] if out["scriptPubKey"]["asm"] == "OP_RETURN 35"), None)
182 else:
183 assert_equal(from_balance_before, from_balance)
184
185 if to_wallet:
186 self.sync_mempools()
187 if add_to_wallet:
188 if not subtract_fee_from_outputs:
189 assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before + Decimal(amount if amount else 0))
190 else:
191 assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before)
192
193 return res
194
195 def run_test(self):
196 self.log.info("Setup wallets...")
197 # w0 is a wallet with coinbase rewards
198 w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
199 # w1 is a regular wallet
200 self.nodes[1].createwallet(wallet_name="w1")
201 w1 = self.nodes[1].get_wallet_rpc("w1")
202 # w2 contains the private keys for w3
203 self.nodes[1].createwallet(wallet_name="w2", blank=True)
204 w2 = self.nodes[1].get_wallet_rpc("w2")
205 xpriv = "tprv8ZgxMBicQKsPfHCsTwkiM1KT56RXbGGTqvc2hgqzycpwbHqqpcajQeMRZoBD35kW4RtyCemu6j34Ku5DEspmgjKdt2qe4SvRch5Kk8B8A2v"
206 xpub = "tpubD6NzVbkrYhZ4YkEfMbRJkQyZe7wTkbTNRECozCtJPtdLRn6cT1QKb8yHjwAPcAr26eHBFYs5iLiFFnCbwPRsncCKUKCfubHDMGKzMVcN1Jg"
207 if self.options.descriptors:
208 w2.importdescriptors([{
209 "desc": descsum_create("wpkh(" + xpriv + "/0/0/*)"),
210 "timestamp": "now",
211 "range": [0, 100],
212 "active": True
213 },{
214 "desc": descsum_create("wpkh(" + xpriv + "/0/1/*)"),
215 "timestamp": "now",
216 "range": [0, 100],
217 "active": True,
218 "internal": True
219 }])
220 else:
221 w2.sethdseed(True)
222
223 # w3 is a watch-only wallet, based on w2
224 self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
225 w3 = self.nodes[1].get_wallet_rpc("w3")
226 if self.options.descriptors:
227 # Match the privkeys in w2 for descriptors
228 res = w3.importdescriptors([{
229 "desc": descsum_create("wpkh(" + xpub + "/0/0/*)"),
230 "timestamp": "now",
231 "range": [0, 100],
232 "keypool": True,
233 "active": True,
234 "watchonly": True
235 },{
236 "desc": descsum_create("wpkh(" + xpub + "/0/1/*)"),
237 "timestamp": "now",
238 "range": [0, 100],
239 "keypool": True,
240 "active": True,
241 "internal": True,
242 "watchonly": True
243 }])
244 assert_equal(res, [{"success": True}, {"success": True}])
245
246 for _ in range(3):
247 a2_receive = w2.getnewaddress()
248 if not self.options.descriptors:
249 # Because legacy wallets use exclusively hardened derivation, we can't do a ranged import like we do for descriptors
250 a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
251 res = w3.importmulti([{
252 "desc": w2.getaddressinfo(a2_receive)["desc"],
253 "timestamp": "now",
254 "keypool": True,
255 "watchonly": True
256 },{
257 "desc": w2.getaddressinfo(a2_change)["desc"],
258 "timestamp": "now",
259 "keypool": True,
260 "internal": True,
261 "watchonly": True
262 }])
263 assert_equal(res, [{"success": True}, {"success": True}])
264
265 w0.sendtoaddress(a2_receive, 10) # fund w3
266 self.generate(self.nodes[0], 1)
267
268 if not self.options.descriptors:
269 # w4 has private keys enabled, but only contains watch-only keys (from w2)
270 # This is legacy wallet behavior only as descriptor wallets don't allow watchonly and non-watchonly things in the same wallet.
271 self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
272 w4 = self.nodes[1].get_wallet_rpc("w4")
273 for _ in range(3):
274 a2_receive = w2.getnewaddress()
275 res = w4.importmulti([{
276 "desc": w2.getaddressinfo(a2_receive)["desc"],
277 "timestamp": "now",
278 "keypool": False,
279 "watchonly": True
280 }])
281 assert_equal(res, [{"success": True}])
282
283 w0.sendtoaddress(a2_receive, 10) # fund w4
284 self.generate(self.nodes[0], 1)
285
286 self.log.info("Send to address...")
287 self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
288 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True)
289
290 self.log.info("Don't broadcast...")
291 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
292 assert res["hex"]
293
294 self.log.info("Return PSBT...")
295 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True)
296 assert res["psbt"]
297
298 self.log.info("Create transaction that spends to address, but don't broadcast...")
299 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
300 # conf_target & estimate_mode can be set as argument or option
301 res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical", add_to_wallet=False)
302 res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=1, estimate_mode="economical", add_to_wallet=False)
303 assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],
304 self.nodes[1].decodepsbt(res2["psbt"])["fee"])
305 # but not at the same time
306 for mode in ["unset", "economical", "conservative"]:
307 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical",
308 conf_target=1, estimate_mode=mode, add_to_wallet=False,
309 expect_error=(-8, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both"))
310
311 self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")
312 res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)
313 res = w2.walletprocesspsbt(res["psbt"])
314 assert res["complete"]
315
316 # verify that fee estimation modes parse case insensitively
317 self.log.info("Testing case insensitive fee estimation mode parse")
318 for mode in ["ecoNOMICAL", "economical", "ECONOMICAL"]:
319 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
320 estimate_mode=mode, conf_target=1, add_to_wallet=False
321 )
322 assert_equal(res["complete"], True)
323
324 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
325 arg_estimate_mode=mode, arg_conf_target=1, add_to_wallet=False
326 )
327 assert_equal(res["complete"], True)
328
329 # Verify that different variations of 'unset' still counts as
330 # not setting the estimation mode
331 for mode in ["unSET", "unset", "UNSET"]:
332 self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
333 arg_estimate_mode=mode, arg_conf_target=1, add_to_wallet=False, expect_error = (-8, 'Specify estimate_mode')
334 )
335
336 self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
337 estimate_mode=mode, conf_target=1, add_to_wallet=False, expect_error = (-8, 'Specify estimate_mode')
338 )
339
340 # Verify that 'estimate_mode' requires a confirmation target
341 for mode in ["ecoNOMICAL", "economical", "ECONOMICAL"]:
342 self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
343 estimate_mode=mode, conf_target=None, add_to_wallet=False, expect_error = (-8, 'estimate_mode should be passed with conf_target')
344 )
345
346 self.test_send(from_wallet=w0, to_wallet=w1, amount=1,
347 arg_estimate_mode=mode, arg_conf_target=None, add_to_wallet=False, expect_error = (-8, 'estimate_mode should be passed with conf_target')
348 )
349
350 if not self.options.descriptors:
351 # Descriptor wallets do not allow mixed watch-only and non-watch-only things in the same wallet.
352 # This is specifically testing that w4 ignores its own private keys and creates a psbt with send
353 # which is not something that needs to be tested in descriptor wallets.
354 self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
355 self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
356 res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
357 res = w2.walletprocesspsbt(res["psbt"])
358 assert res["complete"]
359
360 self.log.info("Create OP_RETURN...")
361 self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
362 self.test_send(from_wallet=w0, data="Hello World", expect_error=(-8, "Data must be hexadecimal string (not 'Hello World')"))
363 self.test_send(from_wallet=w0, data="23")
364 res = self.test_send(from_wallet=w3, data="23")
365 res = w2.walletprocesspsbt(res["psbt"])
366 assert res["complete"]
367
368 self.log.info("Test setting explicit fee rate")
369 res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate="1", add_to_wallet=False)
370 res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate="1", add_to_wallet=False)
371 assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"], self.nodes[1].decodepsbt(res2["psbt"])["fee"])
372
373 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=7, add_to_wallet=False)
374 fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
375 assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00007"))
376
377 # "unset" and None are treated the same for estimate_mode
378 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=2, estimate_mode="unset", add_to_wallet=False)
379 fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
380 assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00002"))
381
382 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=4.531, add_to_wallet=False)
383 fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
384 assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00004531"))
385
386 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=3, add_to_wallet=False)
387 fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
388 assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00003"))
389
390 # Test that passing fee_rate as both an argument and an option raises.
391 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=1, fee_rate=1, add_to_wallet=False,
392 expect_error=(-8, "Pass the fee_rate either as an argument, or in the options object, but not both"))
393
394 assert_raises_rpc_error(-8, "Use fee_rate (sat/vB) instead of feeRate", w0.send, {w1.getnewaddress(): 1}, 6, "conservative", 1, {"feeRate": 0.01})
395
396 assert_raises_rpc_error(-3, "Unexpected key totalFee", w0.send, {w1.getnewaddress(): 1}, 6, "conservative", 1, {"totalFee": 0.01})
397
398 for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):
399 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=target, estimate_mode=mode,
400 expect_error=(-8, "Invalid conf_target, must be between 1 and 1008")) # max value of 1008 per src/policy/fees.h
401 msg = 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"'
402 for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):
403 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=target, estimate_mode=mode, expect_error=(-8, msg))
404 for mode in ["", "foo", Decimal("3.141592")]:
405 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode=mode, expect_error=(-8, msg))
406 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=0.1, arg_estimate_mode=mode, expect_error=(-8, msg))
407 assert_raises_rpc_error(-8, msg, w0.send, {w1.getnewaddress(): 1}, 0.1, mode)
408
409 for mode in ["economical", "conservative"]:
410 for k, v in {"string": "true", "bool": True, "object": {"foo": "bar"}}.items():
411 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode,
412 expect_error=(-3, f"JSON value of type {k} for field conf_target is not of expected type number"))
413
414 # Test setting explicit fee rate just below the minimum of 1 sat/vB.
415 self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed")
416 msg = "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
417 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.999, expect_error=(-4, msg))
418 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.999, expect_error=(-4, msg))
419
420 self.log.info("Explicit fee rate raises if invalid fee_rate is passed")
421 # Test fee_rate with zero values.
422 msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
423 for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
424 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=zero_value, expect_error=(-4, msg))
425 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=zero_value, expect_error=(-4, msg))
426 msg = "Invalid amount"
427 # Test fee_rate values that don't pass fixed-point parsing checks.
428 for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
429 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
430 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
431 # Test fee_rate values that cannot be represented in sat/vB.
432 for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
433 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
434 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
435 # Test fee_rate out of range (negative number).
436 msg = "Amount out of range"
437 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=-1, expect_error=(-3, msg))
438 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=-1, expect_error=(-3, msg))
439 # Test type error.
440 msg = "Amount is not a number or string"
441 for invalid_value in [True, {"foo": "bar"}]:
442 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
443 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
444
445 # TODO: Return hex if fee rate is below -maxmempool
446 # res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)
447 # assert res["hex"]
448 # hex = res["hex"]
449 # res = self.nodes[0].testmempoolaccept([hex])
450 # assert not res[0]["allowed"]
451 # assert_equal(res[0]["reject-reason"], "...") # low fee
452 # assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.000001"))
453
454 self.log.info("If inputs are specified, do not automatically add more...")
455 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[], add_to_wallet=False)
456 assert res["complete"]
457 utxo1 = w0.listunspent()[0]
458 assert_equal(utxo1["amount"], 50)
459 ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \
460 "Please allow other inputs to be automatically selected or include more coins manually"
461 self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1],
462 expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS))
463 self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=False,
464 expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS))
465 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=True, add_to_wallet=False)
466 assert res["complete"]
467
468 self.log.info("Manual change address and position...")
469 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, change_address="not an address",
470 expect_error=(-5, "Change address must be a valid limenka address"))
471 change_address = w0.getnewaddress()
472 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address)
473 assert res["complete"]
474 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)
475 assert res["complete"]
476 assert_equal(self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"], change_address)
477 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)
478 assert res["complete"]
479 change_address = self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"]
480 assert change_address[0] == "m" or change_address[0] == "n"
481
482 self.log.info("Set lock time...")
483 height = self.nodes[0].getblockchaininfo()["blocks"]
484 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, locktime=height + 1)
485 assert res["complete"]
486 assert res["txid"]
487 txid = res["txid"]
488 # Although the wallet finishes the transaction, it can't be added to the mempool yet:
489 hex = self.nodes[0].gettransaction(res["txid"])["hex"]
490 res = self.nodes[0].testmempoolaccept([hex])
491 assert not res[0]["allowed"]
492 assert_equal(res[0]["reject-reason"], "non-final")
493 # It shouldn't be confirmed in the next block
494 self.generate(self.nodes[0], 1)
495 assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 0)
496 # The mempool should allow it now:
497 res = self.nodes[0].testmempoolaccept([hex])
498 assert res[0]["allowed"]
499 # Don't wait for wallet to add it to the mempool:
500 res = self.nodes[0].sendrawtransaction(hex)
501 self.generate(self.nodes[0], 1)
502 assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 1)
503
504 self.log.info("Lock unspents...")
505 utxo1 = w0.listunspent()[0]
506 assert_greater_than(utxo1["amount"], 1)
507 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False, lock_unspents=True)
508 assert res["complete"]
509 locked_coins = w0.listlockunspent()
510 assert_equal(len(locked_coins), 1)
511 # Locked coins are automatically unlocked when manually selected
512 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False)
513 assert res["complete"]
514
515 self.log.info("Replaceable...")
516 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=True)
517 assert res["complete"]
518 assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "yes")
519 res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=False)
520 assert res["complete"]
521 assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "no")
522
523 self.log.info("Subtract fee from output")
524 self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
525
526 self.log.info("Include unsafe inputs")
527 self.nodes[1].createwallet(wallet_name="w5")
528 w5 = self.nodes[1].get_wallet_rpc("w5")
529 self.test_send(from_wallet=w0, to_wallet=w5, amount=2)
530 self.test_send(from_wallet=w5, to_wallet=w0, amount=1, expect_error=(-4, "Insufficient funds"))
531 res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
532 assert res["complete"]
533
534 self.log.info("Minconf")
535 self.nodes[1].createwallet(wallet_name="minconfw")
536 minconfw= self.nodes[1].get_wallet_rpc("minconfw")
537 self.test_send(from_wallet=w0, to_wallet=minconfw, amount=2)
538 self.generate(self.nodes[0], 3)
539 self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=4, expect_error=(-4, "Insufficient funds"))
540 self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=-4, expect_error=(-8, "Negative minconf"))
541 res = self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=3)
542 assert res["complete"]
543
544 self.log.info("External outputs")
545 privkey, _ = generate_keypair(wif=True)
546
547 self.nodes[1].createwallet("extsend")
548 ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
549 self.nodes[1].createwallet("extfund")
550 ext_fund = self.nodes[1].get_wallet_rpc("extfund")
551
552 # Make a weird but signable script. sh(wsh(pkh())) descriptor accomplishes this
553 desc = descsum_create("sh(wsh(pkh({})))".format(privkey))
554 if self.options.descriptors:
555 res = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])
556 else:
557 res = ext_fund.importmulti([{"desc": desc, "timestamp": "now"}])
558 assert res[0]["success"]
559 addr = self.nodes[0].deriveaddresses(desc)[0]
560 addr_info = ext_fund.getaddressinfo(addr)
561
562 self.nodes[0].sendtoaddress(addr, 10)
563 self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)
564 self.generate(self.nodes[0], 6)
565 ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
566
567 # An external input without solving data should result in an error
568 self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Not solvable pre-selected input COutPoint(%s, %s)" % (ext_utxo["txid"][0:10], ext_utxo["vout"])))
569
570 # But funding should work when the solving data is provided
571 res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]})
572 signed = ext_wallet.walletprocesspsbt(res["psbt"])
573 signed = ext_fund.walletprocesspsbt(res["psbt"])
574 assert signed["complete"]
575
576 res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"descriptors": [desc]})
577 signed = ext_wallet.walletprocesspsbt(res["psbt"])
578 signed = ext_fund.walletprocesspsbt(res["psbt"])
579 assert signed["complete"]
580
581 dec = self.nodes[0].decodepsbt(signed["psbt"])
582 for i, txin in enumerate(dec["tx"]["vin"]):
583 if txin["txid"] == ext_utxo["txid"] and txin["vout"] == ext_utxo["vout"]:
584 input_idx = i
585 break
586 psbt_in = dec["inputs"][input_idx]
587 scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
588 witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
589 input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
590
591 # Input weight error conditions
592 assert_raises_rpc_error(
593 -8,
594 "Input weights should be specified in inputs rather than in options.",
595 ext_wallet.send,
596 outputs={self.nodes[0].getnewaddress(): 15},
597 options={"inputs": [ext_utxo], "input_weights": [{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": 1000}]}
598 )
599
600 target_fee_rate_sat_vb = 10
601 # Funding should also work when input weights are provided
602 res = self.test_send(
603 from_wallet=ext_wallet,
604 to_wallet=self.nodes[0],
605 amount=15,
606 inputs=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}],
607 add_inputs=True,
608 psbt=True,
609 include_watching=True,
610 fee_rate=target_fee_rate_sat_vb
611 )
612 signed = ext_wallet.walletprocesspsbt(res["psbt"])
613 signed = ext_fund.walletprocesspsbt(res["psbt"])
614 assert signed["complete"]
615 testres = self.nodes[0].testmempoolaccept([signed["hex"]])[0]
616 assert_equal(testres["allowed"], True)
617 actual_fee_rate_sat_vb = Decimal(testres["fees"]["base"]) * Decimal(1e8) / Decimal(testres["vsize"])
618 # Due to ECDSA signatures not always being the same length, the actual fee rate may be slightly different
619 # but rounded to nearest integer, it should be the same as the target fee rate
620 assert_equal(round(actual_fee_rate_sat_vb), target_fee_rate_sat_vb)
621
622 # Check tx creation size limits
623 self.test_weight_limits()
624
625 def test_weight_limits(self):
626 self.log.info("Test weight limits")
627
628 self.nodes[1].createwallet("test_weight_limits")
629 wallet = self.nodes[1].get_wallet_rpc("test_weight_limits")
630
631 # Generate future inputs; 272 WU per input (273 when high-s).
632 # Picking 1471 inputs will exceed the max standard tx weight.
633 outputs = []
634 for _ in range(1472):
635 outputs.append({wallet.getnewaddress(address_type="legacy"): 0.1})
636 self.nodes[0].send(outputs=outputs)
637 self.generate(self.nodes[0], 1)
638
639 # 1) Try to fund transaction only using the preset inputs
640 inputs = wallet.listunspent()
641 assert_raises_rpc_error(-4, "Transaction too large",
642 wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}], options={"inputs": inputs, "add_inputs": False})
643
644 # 2) Let the wallet fund the transaction
645 assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
646 wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}])
647
648 # 3) Pre-select some inputs and let the wallet fill-up the remaining amount
649 inputs = inputs[0:1000]
650 assert_raises_rpc_error(-4, "The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
651 wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}], options={"inputs": inputs, "add_inputs": True})
652
653 self.nodes[1].unloadwallet("test_weight_limits")
654
655
656 if __name__ == '__main__':
657 WalletSendTest(__file__).main()
658