wallet_signrawtransactionwithwallet.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2015-present 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 """Test transaction signing using the signrawtransactionwithwallet RPC."""
6
7 from test_framework.blocktools import (
8 COINBASE_MATURITY,
9 )
10 from test_framework.address import (
11 script_to_p2wsh,
12 )
13 from test_framework.test_framework import BitcoinTestFramework
14 from test_framework.util import (
15 assert_equal,
16 assert_raises_rpc_error,
17 )
18 from test_framework.messages import (
19 CTxInWitness,
20 tx_from_hex,
21 )
22 from test_framework.script import (
23 CScript,
24 OP_CHECKLOCKTIMEVERIFY,
25 OP_CHECKSEQUENCEVERIFY,
26 OP_DROP,
27 OP_TRUE,
28 )
29
30 from decimal import (
31 Decimal,
32 getcontext,
33 )
34
35
36 RAW_TX = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000'
37
38
39 class SignRawTransactionWithWalletTest(BitcoinTestFramework):
40 def set_test_params(self):
41 self.setup_clean_chain = True
42 self.num_nodes = 2
43
44 def skip_test_if_missing_module(self):
45 self.skip_if_no_wallet()
46
47 def test_with_lock_outputs(self):
48 self.log.info("Test correct error reporting when trying to sign a locked output")
49 self.nodes[0].encryptwallet("password")
50 assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signrawtransactionwithwallet, RAW_TX)
51 self.nodes[0].walletpassphrase("password", 9999)
52
53 def test_with_invalid_sighashtype(self):
54 self.log.info("Test signrawtransactionwithwallet raises if an invalid sighashtype is passed")
55 assert_raises_rpc_error(-8, "'all' is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithwallet, hexstring=RAW_TX, sighashtype="all")
56
57 def script_verification_error_test(self):
58 """Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script.
59
60 Expected results:
61
62 3) The transaction has no complete set of signatures
63 4) Two script verification errors occurred
64 5) Script verification errors have certain properties ("txid", "vout", "scriptSig", "sequence", "error")
65 6) The verification errors refer to the invalid (vin 1) and missing input (vin 2)"""
66 self.log.info("Test script verification errors")
67 privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N']
68
69 inputs = [
70 # Valid pay-to-pubkey script
71 {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0},
72 # Invalid script
73 {'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', 'vout': 7},
74 # Missing scriptPubKey
75 {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 1},
76 ]
77
78 scripts = [
79 # Valid pay-to-pubkey script
80 {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0,
81 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'},
82 # Invalid script
83 {'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', 'vout': 7,
84 'scriptPubKey': 'badbadbadbad'}
85 ]
86
87 outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
88
89 rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
90
91 # Make sure decoderawtransaction is at least marginally sane
92 decodedRawTx = self.nodes[0].decoderawtransaction(rawTx)
93 for i, inp in enumerate(inputs):
94 assert_equal(decodedRawTx["vin"][i]["txid"], inp["txid"])
95 assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"])
96
97 # Make sure decoderawtransaction throws if there is extra data
98 assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00")
99
100 rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, scripts)
101
102 # 3) The transaction has no complete set of signatures
103 assert not rawTxSigned['complete']
104
105 # 4) Two script verification errors occurred
106 assert 'errors' in rawTxSigned
107 assert_equal(len(rawTxSigned['errors']), 2)
108
109 # 5) Script verification errors have certain properties
110 assert 'txid' in rawTxSigned['errors'][0]
111 assert 'vout' in rawTxSigned['errors'][0]
112 assert 'witness' in rawTxSigned['errors'][0]
113 assert 'scriptSig' in rawTxSigned['errors'][0]
114 assert 'sequence' in rawTxSigned['errors'][0]
115 assert 'error' in rawTxSigned['errors'][0]
116
117 # 6) The verification errors refer to the invalid (vin 1) and missing input (vin 2)
118 assert_equal(rawTxSigned['errors'][0]['txid'], inputs[1]['txid'])
119 assert_equal(rawTxSigned['errors'][0]['vout'], inputs[1]['vout'])
120 assert_equal(rawTxSigned['errors'][1]['txid'], inputs[2]['txid'])
121 assert_equal(rawTxSigned['errors'][1]['vout'], inputs[2]['vout'])
122 assert not rawTxSigned['errors'][0]['witness']
123
124 # Now test signing failure for transaction with input witnesses
125 p2wpkh_raw_tx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"
126
127 rawTxSigned = self.nodes[0].signrawtransactionwithwallet(p2wpkh_raw_tx)
128
129 # 7) The transaction has no complete set of signatures
130 assert not rawTxSigned['complete']
131
132 # 8) Two script verification errors occurred
133 assert 'errors' in rawTxSigned
134 assert_equal(len(rawTxSigned['errors']), 2)
135
136 # 9) Script verification errors have certain properties
137 assert 'txid' in rawTxSigned['errors'][0]
138 assert 'vout' in rawTxSigned['errors'][0]
139 assert 'witness' in rawTxSigned['errors'][0]
140 assert 'scriptSig' in rawTxSigned['errors'][0]
141 assert 'sequence' in rawTxSigned['errors'][0]
142 assert 'error' in rawTxSigned['errors'][0]
143
144 # Non-empty witness checked here
145 assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"])
146 assert not rawTxSigned['errors'][0]['witness']
147
148 def test_fully_signed_tx(self):
149 self.log.info("Test signing a fully signed transaction does nothing")
150 self.nodes[0].walletpassphrase("password", 9999)
151 self.generate(self.nodes[0], COINBASE_MATURITY + 1)
152 rawtx = self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 10}])
153 fundedtx = self.nodes[0].fundrawtransaction(rawtx)
154 signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"])
155 assert_equal(signedtx["complete"], True)
156 signedtx2 = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])
157 assert_equal(signedtx2["complete"], True)
158 assert_equal(signedtx["hex"], signedtx2["hex"])
159 self.nodes[0].walletlock()
160
161 def OP_1NEGATE_test(self):
162 self.log.info("Test OP_1NEGATE (0x4f) satisfies BIP62 minimal push standardness rule")
163 hex_str = (
164 "0200000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
165 "FFFFFFFF00000000044F024F9CFDFFFFFF01F0B9F5050000000023210277777777"
166 "77777777777777777777777777777777777777777777777777777777AC66030000"
167 )
168 prev_txs = [
169 {
170 "txid": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
171 "vout": 0,
172 "scriptPubKey": "A914AE44AB6E9AA0B71F1CD2B453B69340E9BFBAEF6087",
173 "redeemScript": "4F9C",
174 "amount": 1,
175 }
176 ]
177 txn = self.nodes[0].signrawtransactionwithwallet(hex_str, prev_txs)
178 assert txn["complete"]
179
180 def test_signing_with_csv(self):
181 self.log.info("Test signing a transaction containing a fully signed CSV input")
182 self.nodes[0].walletpassphrase("password", 9999)
183 getcontext().prec = 8
184
185 # Make sure CSV is active
186 assert self.nodes[0].getdeploymentinfo()['deployments']['csv']['active']
187
188 # Create a P2WSH script with CSV
189 script = CScript([1, OP_CHECKSEQUENCEVERIFY, OP_DROP])
190 address = script_to_p2wsh(script)
191
192 # Fund that address and make the spend
193 utxo1 = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
194 self.generate(self.nodes[0], 1)
195 utxo2 = self.nodes[0].listunspent()[0]
196 amt = Decimal(1) + utxo2["amount"] - Decimal(0.00001)
197 tx = self.nodes[0].createrawtransaction(
198 [{**utxo1, "sequence": 1},{"txid": utxo2["txid"], "vout": utxo2["vout"]}],
199 [{self.nodes[0].getnewaddress(): amt}],
200 self.nodes[0].getblockcount()
201 )
202
203 # Set the witness script
204 ctx = tx_from_hex(tx)
205 ctx.wit.vtxinwit.append(CTxInWitness())
206 ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
207 tx = ctx.serialize_with_witness().hex()
208
209 # Sign and send the transaction
210 signed = self.nodes[0].signrawtransactionwithwallet(tx)
211 assert_equal(signed["complete"], True)
212 self.nodes[0].sendrawtransaction(signed["hex"])
213
214 def test_signing_with_cltv(self):
215 self.log.info("Test signing a transaction containing a fully signed CLTV input")
216 self.nodes[0].walletpassphrase("password", 9999)
217 getcontext().prec = 8
218
219 # Make sure CLTV is active
220 assert self.nodes[0].getdeploymentinfo()['deployments']['bip65']['active']
221
222 # Create a P2WSH script with CLTV
223 script = CScript([100, OP_CHECKLOCKTIMEVERIFY, OP_DROP])
224 address = script_to_p2wsh(script)
225
226 # Fund that address and make the spend
227 utxo1 = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
228 self.generate(self.nodes[0], 1)
229 utxo2 = self.nodes[0].listunspent()[0]
230 amt = Decimal(1) + utxo2["amount"] - Decimal(0.00001)
231 tx = self.nodes[0].createrawtransaction(
232 [utxo1, {"txid": utxo2["txid"], "vout": utxo2["vout"]}],
233 [{self.nodes[0].getnewaddress(): amt}],
234 self.nodes[0].getblockcount()
235 )
236
237 # Set the witness script
238 ctx = tx_from_hex(tx)
239 ctx.wit.vtxinwit.append(CTxInWitness())
240 ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
241 tx = ctx.serialize_with_witness().hex()
242
243 # Sign and send the transaction
244 signed = self.nodes[0].signrawtransactionwithwallet(tx)
245 assert_equal(signed["complete"], True)
246 self.nodes[0].sendrawtransaction(signed["hex"])
247
248 def test_signing_with_missing_prevtx_info(self):
249 txid = "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000"
250 for type in ["bech32", "p2sh-segwit", "legacy"]:
251 self.log.info(f"Test signing with missing prevtx info ({type})")
252 addr = self.nodes[0].getnewaddress("", type)
253 addrinfo = self.nodes[0].getaddressinfo(addr)
254 pubkey = addrinfo["scriptPubKey"]
255 inputs = [{'txid': txid, 'vout': 3, 'sequence': 1000}]
256 outputs = {self.nodes[0].getnewaddress(): 1}
257 rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
258
259 prevtx = dict(txid=txid, scriptPubKey=pubkey, vout=3, amount=1)
260 succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
261 assert succ["complete"]
262
263 if type == "legacy":
264 del prevtx["amount"]
265 succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx])
266 assert succ["complete"]
267 else:
268 assert_raises_rpc_error(-3, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [
269 {
270 "txid": txid,
271 "scriptPubKey": pubkey,
272 "vout": 3,
273 }
274 ])
275
276 assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [
277 {
278 "txid": txid,
279 "scriptPubKey": pubkey,
280 "amount": 1,
281 }
282 ])
283 assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [
284 {
285 "scriptPubKey": pubkey,
286 "vout": 3,
287 "amount": 1,
288 }
289 ])
290 assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [
291 {
292 "txid": txid,
293 "vout": 3,
294 "amount": 1
295 }
296 ])
297
298 def run_test(self):
299 self.script_verification_error_test()
300 self.OP_1NEGATE_test()
301 self.test_with_lock_outputs()
302 self.test_with_invalid_sighashtype()
303 self.test_fully_signed_tx()
304 self.test_signing_with_csv()
305 self.test_signing_with_cltv()
306 self.test_signing_with_missing_prevtx_info()
307
308
309 if __name__ == '__main__':
310 SignRawTransactionWithWalletTest(__file__).main()
311