|
| 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 signrawtransactionwithkey RPC.""" |
| 6 | + |
| 7 | +from test_framework.blocktools import ( |
| 8 | + COINBASE_MATURITY, |
| 9 | +) |
| 10 | +from test_framework.address import ( |
| 11 | + script_to_p2sh, |
| 12 | +) |
| 13 | +from test_framework.key import ECKey |
| 14 | +from test_framework.test_framework import BitcoinTestFramework |
| 15 | +from test_framework.util import ( |
| 16 | + assert_equal, |
| 17 | + find_vout_for_address, |
| 18 | +) |
| 19 | +from test_framework.script_util import ( |
| 20 | + key_to_p2pk_script, |
| 21 | + key_to_p2pkh_script, |
| 22 | + script_to_p2sh_p2wsh_script, |
| 23 | + script_to_p2wsh_script, |
| 24 | +) |
| 25 | +from test_framework.wallet_util import ( |
| 26 | + bytes_to_wif, |
| 27 | +) |
| 28 | + |
| 29 | +from decimal import ( |
| 30 | + Decimal, |
| 31 | +) |
| 32 | +from test_framework.wallet import ( |
| 33 | + getnewdestination, |
| 34 | +) |
| 35 | + |
| 36 | + |
| 37 | +class SignRawTransactionWithKeyTest(BitcoinTestFramework): |
| 38 | + def set_test_params(self): |
| 39 | + self.setup_clean_chain = True |
| 40 | + self.num_nodes = 2 |
| 41 | + |
| 42 | + def send_to_address(self, addr, amount): |
| 43 | + input = {"txid": self.nodes[0].getblock(self.block_hash[self.blk_idx])["tx"][0], "vout": 0} |
| 44 | + output = {addr: amount} |
| 45 | + self.blk_idx += 1 |
| 46 | + rawtx = self.nodes[0].createrawtransaction([input], output) |
| 47 | + txid = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithkey(rawtx, [self.nodes[0].get_deterministic_priv_key().key])["hex"], 0) |
| 48 | + return txid |
| 49 | + |
| 50 | + def successful_signing_test(self): |
| 51 | + """Create and sign a valid raw transaction with one input. |
| 52 | +
|
| 53 | + Expected results: |
| 54 | +
|
| 55 | + 1) The transaction has a complete set of signatures |
| 56 | + 2) No script verification error occurred""" |
| 57 | + self.log.info("Test valid raw transaction with one input") |
| 58 | + privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N', 'cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA'] |
| 59 | + |
| 60 | + inputs = [ |
| 61 | + # Valid pay-to-pubkey scripts |
| 62 | + {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0, |
| 63 | + 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}, |
| 64 | + {'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', 'vout': 0, |
| 65 | + 'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'}, |
| 66 | + ] |
| 67 | + |
| 68 | + outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1} |
| 69 | + |
| 70 | + rawTx = self.nodes[0].createrawtransaction(inputs, outputs) |
| 71 | + rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, inputs) |
| 72 | + |
| 73 | + # 1) The transaction has a complete set of signatures |
| 74 | + assert rawTxSigned['complete'] |
| 75 | + |
| 76 | + # 2) No script verification error occurred |
| 77 | + assert 'errors' not in rawTxSigned |
| 78 | + |
| 79 | + def witness_script_test(self): |
| 80 | + self.log.info("Test signing transaction to P2SH-P2WSH addresses without wallet") |
| 81 | + # Create a new P2SH-P2WSH 1-of-1 multisig address: |
| 82 | + eckey = ECKey() |
| 83 | + eckey.generate() |
| 84 | + embedded_privkey = bytes_to_wif(eckey.get_bytes()) |
| 85 | + embedded_pubkey = eckey.get_pubkey().get_bytes().hex() |
| 86 | + p2sh_p2wsh_address = self.nodes[1].createmultisig(1, [embedded_pubkey], "p2sh-segwit") |
| 87 | + # send transaction to P2SH-P2WSH 1-of-1 multisig address |
| 88 | + self.block_hash = self.generate(self.nodes[0], COINBASE_MATURITY + 1) |
| 89 | + self.blk_idx = 0 |
| 90 | + self.send_to_address(p2sh_p2wsh_address["address"], 49.999) |
| 91 | + self.generate(self.nodes[0], 1) |
| 92 | + # Get the UTXO info from scantxoutset |
| 93 | + unspent_output = self.nodes[1].scantxoutset('start', [p2sh_p2wsh_address['descriptor']])['unspents'][0] |
| 94 | + spk = script_to_p2sh_p2wsh_script(p2sh_p2wsh_address['redeemScript']).hex() |
| 95 | + unspent_output['witnessScript'] = p2sh_p2wsh_address['redeemScript'] |
| 96 | + unspent_output['redeemScript'] = script_to_p2wsh_script(unspent_output['witnessScript']).hex() |
| 97 | + assert_equal(spk, unspent_output['scriptPubKey']) |
| 98 | + # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys |
| 99 | + spending_tx = self.nodes[0].createrawtransaction([unspent_output], {getnewdestination()[2]: Decimal("49.998")}) |
| 100 | + spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output]) |
| 101 | + # Check the signing completed successfully |
| 102 | + assert 'complete' in spending_tx_signed |
| 103 | + assert_equal(spending_tx_signed['complete'], True) |
| 104 | + |
| 105 | + # Now test with P2PKH and P2PK scripts as the witnessScript |
| 106 | + for tx_type in ['P2PKH', 'P2PK']: # these tests are order-independent |
| 107 | + self.verify_txn_with_witness_script(tx_type) |
| 108 | + |
| 109 | + def verify_txn_with_witness_script(self, tx_type): |
| 110 | + self.log.info("Test with a {} script as the witnessScript".format(tx_type)) |
| 111 | + eckey = ECKey() |
| 112 | + eckey.generate() |
| 113 | + embedded_privkey = bytes_to_wif(eckey.get_bytes()) |
| 114 | + embedded_pubkey = eckey.get_pubkey().get_bytes().hex() |
| 115 | + witness_script = { |
| 116 | + 'P2PKH': key_to_p2pkh_script(embedded_pubkey).hex(), |
| 117 | + 'P2PK': key_to_p2pk_script(embedded_pubkey).hex() |
| 118 | + }.get(tx_type, "Invalid tx_type") |
| 119 | + redeem_script = script_to_p2wsh_script(witness_script).hex() |
| 120 | + addr = script_to_p2sh(redeem_script) |
| 121 | + script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey'] |
| 122 | + # Fund that address |
| 123 | + txid = self.send_to_address(addr, 10) |
| 124 | + vout = find_vout_for_address(self.nodes[0], txid, addr) |
| 125 | + self.generate(self.nodes[0], 1) |
| 126 | + # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys |
| 127 | + spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {getnewdestination()[2]: Decimal("9.999")}) |
| 128 | + spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}]) |
| 129 | + # Check the signing completed successfully |
| 130 | + assert 'complete' in spending_tx_signed |
| 131 | + assert_equal(spending_tx_signed['complete'], True) |
| 132 | + self.nodes[0].sendrawtransaction(spending_tx_signed['hex']) |
| 133 | + |
| 134 | + def run_test(self): |
| 135 | + self.successful_signing_test() |
| 136 | + self.witness_script_test() |
| 137 | + |
| 138 | + |
| 139 | +if __name__ == '__main__': |
| 140 | + SignRawTransactionWithKeyTest().main() |
0 commit comments