Merge #14481: Add P2SH-P2WSH support to listunspent RPC
6ca836ab3a
Add release note for listunspent P2WSH change (MeshCollider)928beae007
Add test for P2SH-P2WSH in signrawtransactionwithkey and listunspent (MeshCollider)314784a60f
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
This commit is contained in:
commit
3facd9fdc4
4 changed files with 81 additions and 10 deletions
9
doc/release-notes-14481.md
Normal file
9
doc/release-notes-14481.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
Low-level RPC changes
|
||||
----------------------
|
||||
|
||||
The `listunspent` RPC has been modified so that it also returns `witnessScript`,
|
||||
the witness script in the case of a P2WSH or P2SH-P2WSH output.
|
||||
|
||||
The `signrawtransactionwithkey` and `signrawtransactionwithwallet` RPCs have been
|
||||
modified so that they also optionally accept a `witnessScript`, the witness script in the
|
||||
case of a P2WSH or P2SH-P2WSH output. This is compatible with the change to `listunspent`.
|
|
@ -855,15 +855,25 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
|
|||
RPCTypeCheckObj(prevOut,
|
||||
{
|
||||
{"redeemScript", UniValueType(UniValue::VSTR)},
|
||||
});
|
||||
UniValue v = find_value(prevOut, "redeemScript");
|
||||
if (!v.isNull()) {
|
||||
std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
|
||||
{"witnessScript", UniValueType(UniValue::VSTR)},
|
||||
}, true);
|
||||
UniValue rs = find_value(prevOut, "redeemScript");
|
||||
if (!rs.isNull()) {
|
||||
std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
keystore->AddCScript(redeemScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
// This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
|
||||
keystore->AddCScript(GetScriptForWitness(redeemScript));
|
||||
}
|
||||
UniValue ws = find_value(prevOut, "witnessScript");
|
||||
if (!ws.isNull()) {
|
||||
std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
|
||||
CScript witnessScript(wsData.begin(), wsData.end());
|
||||
keystore->AddCScript(witnessScript);
|
||||
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
|
||||
keystore->AddCScript(GetScriptForWitness(witnessScript));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -948,7 +958,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
|||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
|
||||
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
|
||||
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
|
||||
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"},
|
||||
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
|
||||
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
|
||||
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2770,7 +2770,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
|||
" \"scriptPubKey\" : \"key\", (string) the script key\n"
|
||||
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
|
||||
" \"confirmations\" : n, (numeric) The number of confirmations\n"
|
||||
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
|
||||
" \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH"
|
||||
" \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
|
||||
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
|
||||
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
|
||||
" \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
|
||||
|
@ -2884,6 +2885,28 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
|||
CScript redeemScript;
|
||||
if (pwallet->GetCScript(hash, redeemScript)) {
|
||||
entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
|
||||
// Now check if the redeemScript is actually a P2WSH script
|
||||
CTxDestination witness_destination;
|
||||
if (redeemScript.IsPayToWitnessScriptHash()) {
|
||||
bool extracted = ExtractDestination(redeemScript, witness_destination);
|
||||
assert(extracted);
|
||||
// Also return the witness script
|
||||
const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
|
||||
CScriptID id;
|
||||
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
|
||||
CScript witnessScript;
|
||||
if (pwallet->GetCScript(id, witnessScript)) {
|
||||
entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (scriptPubKey.IsPayToWitnessScriptHash()) {
|
||||
const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
|
||||
CScriptID id;
|
||||
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
|
||||
CScript witnessScript;
|
||||
if (pwallet->GetCScript(id, witnessScript)) {
|
||||
entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3139,7 +3162,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
|
|||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
|
||||
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
|
||||
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
|
||||
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"},
|
||||
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
|
||||
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
|
||||
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
"""Test transaction signing using the signrawtransaction* RPCs."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal, assert_raises_rpc_error
|
||||
from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, hex_str_to_bytes
|
||||
from test_framework.messages import sha256
|
||||
from test_framework.script import CScript, OP_0
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
class SignRawTransactionsTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.extra_args = [["-deprecatedrpc=signrawtransaction"]]
|
||||
self.num_nodes = 2
|
||||
self.extra_args = [["-deprecatedrpc=signrawtransaction"], []]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
@ -143,9 +146,33 @@ class SignRawTransactionsTest(BitcoinTestFramework):
|
|||
assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"])
|
||||
assert not rawTxSigned['errors'][0]['witness']
|
||||
|
||||
def witness_script_test(self):
|
||||
# Now test signing transaction to P2SH-P2WSH addresses without wallet
|
||||
# Create a new P2SH-P2WSH 1-of-1 multisig address:
|
||||
embedded_address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())
|
||||
embedded_privkey = self.nodes[1].dumpprivkey(embedded_address["address"])
|
||||
p2sh_p2wsh_address = self.nodes[1].addmultisigaddress(1, [embedded_address["pubkey"]], "", "p2sh-segwit")
|
||||
# send transaction to P2SH-P2WSH 1-of-1 multisig address
|
||||
self.nodes[0].generate(101)
|
||||
self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999)
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
# Find the UTXO for the transaction node[1] should have received, check witnessScript matches
|
||||
unspent_output = self.nodes[1].listunspent(0, 999999, [p2sh_p2wsh_address["address"]])[0]
|
||||
assert_equal(unspent_output["witnessScript"], p2sh_p2wsh_address["redeemScript"])
|
||||
p2sh_redeemScript = CScript([OP_0, sha256(hex_str_to_bytes(p2sh_p2wsh_address["redeemScript"]))])
|
||||
assert_equal(unspent_output["redeemScript"], bytes_to_hex_str(p2sh_redeemScript))
|
||||
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
|
||||
spending_tx = self.nodes[0].createrawtransaction([unspent_output], {self.nodes[1].getnewaddress(): Decimal("49.998")})
|
||||
spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output])
|
||||
# Check the signing completed successfully
|
||||
assert 'complete' in spending_tx_signed
|
||||
assert_equal(spending_tx_signed['complete'], True)
|
||||
|
||||
def run_test(self):
|
||||
self.successful_signing_test()
|
||||
self.script_verification_error_test()
|
||||
self.witness_script_test()
|
||||
self.test_with_lock_outputs()
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue