Skip to content

Commit cd3b156

Browse files
committed
Correctly compute redeemScript from witnessScript for signrawtransaction
ParsePrevouts uses GetScriptForWitness on the given witnessScript to find the corresponding redeemScript. This is incorrect when the witnessScript is either a P2PK or P2PKH script as it returns the corresponding P2WPK script instead of turning the witnessScript into a P2WSH script. Instead this should make the script a WitnessV0ScriptHash destination and get the script for that. Test cases are also added.
1 parent d52ba21 commit cd3b156

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

src/rpc/rawtransaction_util.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
216216
keystore->AddCScript(script);
217217
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
218218
// This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.
219-
CScript witness_output_script{GetScriptForWitness(script)};
219+
CScript witness_output_script{GetScriptForDestination(WitnessV0ScriptHash(script))};
220220
keystore->AddCScript(witness_output_script);
221221

222222
if (!ws.isNull() && !rs.isNull()) {

test/functional/rpc_signrawtransaction.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test transaction signing using the signrawtransaction* RPCs."""
66

7+
from test_framework.address import check_script, script_to_p2sh
78
from test_framework.test_framework import BitcoinTestFramework
8-
from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes
9+
from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
910
from test_framework.messages import sha256
10-
from test_framework.script import CScript, OP_0
11+
from test_framework.script import CScript, OP_0, OP_CHECKSIG
1112

1213
from decimal import Decimal
1314

@@ -168,6 +169,44 @@ def witness_script_test(self):
168169
assert 'complete' in spending_tx_signed
169170
assert_equal(spending_tx_signed['complete'], True)
170171

172+
# Now try with a P2PKH script as the witnessScript
173+
embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
174+
embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
175+
witness_script = embedded_addr_info['scriptPubKey']
176+
redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
177+
addr = script_to_p2sh(redeem_script)
178+
script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
179+
# Fund that address
180+
txid = self.nodes[0].sendtoaddress(addr, 10)
181+
vout = find_vout_for_address(self.nodes[0], txid, addr)
182+
self.nodes[0].generate(1)
183+
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
184+
spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
185+
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}])
186+
# Check the signing completed successfully
187+
assert 'complete' in spending_tx_signed
188+
assert_equal(spending_tx_signed['complete'], True)
189+
self.nodes[1].sendrawtransaction(spending_tx_signed['hex'])
190+
191+
# Now try with a P2PK script as the witnessScript
192+
embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
193+
embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
194+
witness_script = CScript([hex_str_to_bytes(embedded_addr_info['pubkey']), OP_CHECKSIG]).hex()
195+
redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
196+
addr = script_to_p2sh(redeem_script)
197+
script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
198+
# Fund that address
199+
txid = self.nodes[0].sendtoaddress(addr, 10)
200+
vout = find_vout_for_address(self.nodes[0], txid, addr)
201+
self.nodes[0].generate(1)
202+
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
203+
spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
204+
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}])
205+
# Check the signing completed successfully
206+
assert 'complete' in spending_tx_signed
207+
assert_equal(spending_tx_signed['complete'], True)
208+
self.nodes[1].sendrawtransaction(spending_tx_signed['hex'])
209+
171210
def run_test(self):
172211
self.successful_signing_test()
173212
self.script_verification_error_test()

0 commit comments

Comments
 (0)