Skip to content

Commit 3facd9f

Browse files
committed
Merge #14481: Add P2SH-P2WSH support to listunspent RPC
6ca836a Add release note for listunspent P2WSH change (MeshCollider) 928beae Add test for P2SH-P2WSH in signrawtransactionwithkey and listunspent (MeshCollider) 314784a Make listunspent and signrawtransaction RPCs support witnessScript (MeshCollider) Pull request description: This is a reworked version of #11708 after #12427 and the `signrawtransaction` split. For a P2WSH address, listunspent should return the witness script, and for a P2SH-P2WSH address, it should also return the inner witness script (because SignTransaction will automatically wrap it in P2SH if required). Includes a test which also tests the behaviour of #12427, and release note. Tree-SHA512: a8e72cf16930312bf48ec47e44a68f8d7e26664043c1b4cc0983eb25aec4087e511188ff9a0f181cd7b8a0c068c60d7f1e7e3f226b79e8c48890039dcf57f7b7
2 parents cd8ca8b + 6ca836a commit 3facd9f

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

doc/release-notes-14481.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Low-level RPC changes
2+
----------------------
3+
4+
The `listunspent` RPC has been modified so that it also returns `witnessScript`,
5+
the witness script in the case of a P2WSH or P2SH-P2WSH output.
6+
7+
The `signrawtransactionwithkey` and `signrawtransactionwithwallet` RPCs have been
8+
modified so that they also optionally accept a `witnessScript`, the witness script in the
9+
case of a P2WSH or P2SH-P2WSH output. This is compatible with the change to `listunspent`.

src/rpc/rawtransaction.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -855,15 +855,25 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
855855
RPCTypeCheckObj(prevOut,
856856
{
857857
{"redeemScript", UniValueType(UniValue::VSTR)},
858-
});
859-
UniValue v = find_value(prevOut, "redeemScript");
860-
if (!v.isNull()) {
861-
std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
858+
{"witnessScript", UniValueType(UniValue::VSTR)},
859+
}, true);
860+
UniValue rs = find_value(prevOut, "redeemScript");
861+
if (!rs.isNull()) {
862+
std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
862863
CScript redeemScript(rsData.begin(), rsData.end());
863864
keystore->AddCScript(redeemScript);
864865
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
866+
// This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
865867
keystore->AddCScript(GetScriptForWitness(redeemScript));
866868
}
869+
UniValue ws = find_value(prevOut, "witnessScript");
870+
if (!ws.isNull()) {
871+
std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
872+
CScript witnessScript(wsData.begin(), wsData.end());
873+
keystore->AddCScript(witnessScript);
874+
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
875+
keystore->AddCScript(GetScriptForWitness(witnessScript));
876+
}
867877
}
868878
}
869879
}
@@ -948,7 +958,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
948958
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
949959
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
950960
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
951-
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"},
961+
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
962+
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
952963
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
953964
},
954965
},

src/wallet/rpcwallet.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
27702770
" \"scriptPubKey\" : \"key\", (string) the script key\n"
27712771
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
27722772
" \"confirmations\" : n, (numeric) The number of confirmations\n"
2773-
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
2773+
" \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH"
2774+
" \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
27742775
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
27752776
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
27762777
" \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
@@ -2884,6 +2885,28 @@ static UniValue listunspent(const JSONRPCRequest& request)
28842885
CScript redeemScript;
28852886
if (pwallet->GetCScript(hash, redeemScript)) {
28862887
entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
2888+
// Now check if the redeemScript is actually a P2WSH script
2889+
CTxDestination witness_destination;
2890+
if (redeemScript.IsPayToWitnessScriptHash()) {
2891+
bool extracted = ExtractDestination(redeemScript, witness_destination);
2892+
assert(extracted);
2893+
// Also return the witness script
2894+
const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
2895+
CScriptID id;
2896+
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
2897+
CScript witnessScript;
2898+
if (pwallet->GetCScript(id, witnessScript)) {
2899+
entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
2900+
}
2901+
}
2902+
}
2903+
} else if (scriptPubKey.IsPayToWitnessScriptHash()) {
2904+
const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
2905+
CScriptID id;
2906+
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
2907+
CScript witnessScript;
2908+
if (pwallet->GetCScript(id, witnessScript)) {
2909+
entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
28872910
}
28882911
}
28892912
}
@@ -3139,7 +3162,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
31393162
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
31403163
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
31413164
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
3142-
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"},
3165+
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
3166+
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
31433167
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
31443168
},
31453169
},

test/functional/rpc_signrawtransaction.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
"""Test transaction signing using the signrawtransaction* RPCs."""
66

77
from test_framework.test_framework import BitcoinTestFramework
8-
from test_framework.util import assert_equal, assert_raises_rpc_error
8+
from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, hex_str_to_bytes
9+
from test_framework.messages import sha256
10+
from test_framework.script import CScript, OP_0
911

12+
from decimal import Decimal
1013

1114
class SignRawTransactionsTest(BitcoinTestFramework):
1215
def set_test_params(self):
1316
self.setup_clean_chain = True
14-
self.num_nodes = 1
15-
self.extra_args = [["-deprecatedrpc=signrawtransaction"]]
17+
self.num_nodes = 2
18+
self.extra_args = [["-deprecatedrpc=signrawtransaction"], []]
1619

1720
def skip_test_if_missing_module(self):
1821
self.skip_if_no_wallet()
@@ -143,9 +146,33 @@ def script_verification_error_test(self):
143146
assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"])
144147
assert not rawTxSigned['errors'][0]['witness']
145148

149+
def witness_script_test(self):
150+
# Now test signing transaction to P2SH-P2WSH addresses without wallet
151+
# Create a new P2SH-P2WSH 1-of-1 multisig address:
152+
embedded_address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())
153+
embedded_privkey = self.nodes[1].dumpprivkey(embedded_address["address"])
154+
p2sh_p2wsh_address = self.nodes[1].addmultisigaddress(1, [embedded_address["pubkey"]], "", "p2sh-segwit")
155+
# send transaction to P2SH-P2WSH 1-of-1 multisig address
156+
self.nodes[0].generate(101)
157+
self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999)
158+
self.nodes[0].generate(1)
159+
self.sync_all()
160+
# Find the UTXO for the transaction node[1] should have received, check witnessScript matches
161+
unspent_output = self.nodes[1].listunspent(0, 999999, [p2sh_p2wsh_address["address"]])[0]
162+
assert_equal(unspent_output["witnessScript"], p2sh_p2wsh_address["redeemScript"])
163+
p2sh_redeemScript = CScript([OP_0, sha256(hex_str_to_bytes(p2sh_p2wsh_address["redeemScript"]))])
164+
assert_equal(unspent_output["redeemScript"], bytes_to_hex_str(p2sh_redeemScript))
165+
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
166+
spending_tx = self.nodes[0].createrawtransaction([unspent_output], {self.nodes[1].getnewaddress(): Decimal("49.998")})
167+
spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output])
168+
# Check the signing completed successfully
169+
assert 'complete' in spending_tx_signed
170+
assert_equal(spending_tx_signed['complete'], True)
171+
146172
def run_test(self):
147173
self.successful_signing_test()
148174
self.script_verification_error_test()
175+
self.witness_script_test()
149176
self.test_with_lock_outputs()
150177

151178

0 commit comments

Comments
 (0)