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