wallet_signrawtransactionwithwallet.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2015-2022 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 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().main()