Skip to content

Commit 4f933b3

Browse files
committed
p2wpkh, p2wsh and p2sh-nested scripts in decodescript
plus tests
1 parent d09968f commit 4f933b3

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

src/rpc/rawtransaction.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,38 @@ UniValue decodescript(const JSONRPCRequest& request)
563563
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
564564
// don't return the address for a P2SH of the P2SH.
565565
r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
566+
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
567+
// is a witness program, don't return addresses for a segwit programs.
568+
if (type.get_str().find("witness") == std::string::npos) {
569+
txnouttype which_type;
570+
std::vector<std::vector<unsigned char>> solutions_data;
571+
Solver(script, which_type, solutions_data);
572+
// Uncompressed pubkeys cannot be used with segwit checksigs.
573+
// If the script contains an uncompressed pubkey, skip encoding of a segwit program.
574+
if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) {
575+
for (const auto& solution : solutions_data) {
576+
if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) {
577+
return r;
578+
}
579+
}
580+
}
581+
UniValue sr(UniValue::VOBJ);
582+
CScript segwitScr;
583+
if (which_type == TX_PUBKEY) {
584+
segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end())));
585+
} else if (which_type == TX_PUBKEYHASH) {
586+
segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0]));
587+
} else {
588+
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
589+
// Newer segwit program versions should be considered when then become available.
590+
uint256 scriptHash;
591+
CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
592+
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
593+
}
594+
ScriptPubKeyToUniv(segwitScr, sr, true);
595+
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
596+
r.pushKV("segwit", sr);
597+
}
566598
}
567599

568600
return r;

test/functional/rpc_decodescript.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,47 @@ def decodescript_script_sig(self):
5050
def decodescript_script_pub_key(self):
5151
public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
5252
push_public_key = '21' + public_key
53-
public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777'
53+
public_key_hash = '5dd1d3a048119c27b28293056724d9522f26d945'
5454
push_public_key_hash = '14' + public_key_hash
55+
uncompressed_public_key = '04b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb25e01fc8fde47c96c98a4f3a8123e33a38a50cf9025cc8c4494a518f991792bb7'
56+
push_uncompressed_public_key = '41' + uncompressed_public_key
57+
p2wsh_p2pk_script_hash = 'd8590cf8ea0674cf3d49fd7ca249b85ef7485dea62c138468bddeb20cd6519f7'
5558

5659
# below are test cases for all of the standard transaction types
5760

5861
# 1) P2PK scriptPubKey
5962
# <pubkey> OP_CHECKSIG
6063
rpc_result = self.nodes[0].decodescript(push_public_key + 'ac')
6164
assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm'])
65+
# P2PK is translated to P2WPKH
66+
assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm'])
6267

6368
# 2) P2PKH scriptPubKey
6469
# OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
6570
rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac')
6671
assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm'])
72+
# P2PKH is translated to P2WPKH
73+
assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm'])
6774

6875
# 3) multisig scriptPubKey
6976
# <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG
7077
# just imagine that the pub keys used below are different.
7178
# for our purposes here it does not matter that they are the same even though it is unrealistic.
72-
rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_public_key + push_public_key + '53ae')
79+
multisig_script = '52' + push_public_key + push_public_key + push_public_key + '53ae'
80+
rpc_result = self.nodes[0].decodescript(multisig_script)
7381
assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
82+
# multisig in P2WSH
83+
multisig_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(multisig_script)))
84+
assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm'])
7485

7586
# 4) P2SH scriptPubKey
7687
# OP_HASH160 <Hash160(redeemScript)> OP_EQUAL.
7788
# push_public_key_hash here should actually be the hash of a redeem script.
7889
# but this works the same for purposes of this test.
7990
rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87')
8091
assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm'])
92+
# P2SH does not work in segwit secripts. decodescript should not return a result for it.
93+
assert 'segwit' not in rpc_result
8194

8295
# 5) null data scriptPubKey
8396
# use a signature look-alike here to make sure that we do not decode random data as a signature.
@@ -101,8 +114,49 @@ def decodescript_script_pub_key(self):
101114
# <sender-pubkey> OP_CHECKSIG
102115
#
103116
# lock until block 500,000
104-
rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
117+
cltv_script = '63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac'
118+
rpc_result = self.nodes[0].decodescript(cltv_script)
105119
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
120+
# CLTV script in P2WSH
121+
cltv_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(cltv_script)))
122+
assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm'])
123+
124+
# 7) P2PK scriptPubKey
125+
# <pubkey> OP_CHECKSIG
126+
rpc_result = self.nodes[0].decodescript(push_uncompressed_public_key + 'ac')
127+
assert_equal(uncompressed_public_key + ' OP_CHECKSIG', rpc_result['asm'])
128+
# uncompressed pubkeys are invalid for checksigs in segwit scripts.
129+
# decodescript should not return a P2WPKH equivalent.
130+
assert 'segwit' not in rpc_result
131+
132+
# 8) multisig scriptPubKey with an uncompressed pubkey
133+
# <m> <A pubkey> <B pubkey> <n> OP_CHECKMULTISIG
134+
# just imagine that the pub keys used below are different.
135+
# the purpose of this test is to check that a segwit script is not returned for bare multisig scripts
136+
# with an uncompressed pubkey in them.
137+
rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_uncompressed_public_key +'52ae')
138+
assert_equal('2 ' + public_key + ' ' + uncompressed_public_key + ' 2 OP_CHECKMULTISIG', rpc_result['asm'])
139+
# uncompressed pubkeys are invalid for checksigs in segwit scripts.
140+
# decodescript should not return a P2WPKH equivalent.
141+
assert 'segwit' not in rpc_result
142+
143+
# 9) P2WPKH scriptpubkey
144+
# 0 <PubKeyHash>
145+
rpc_result = self.nodes[0].decodescript('00' + push_public_key_hash)
146+
assert_equal('0 ' + public_key_hash, rpc_result['asm'])
147+
# segwit scripts do not work nested into each other.
148+
# a nested segwit script should not be returned in the results.
149+
assert 'segwit' not in rpc_result
150+
151+
# 10) P2WSH scriptpubkey
152+
# 0 <ScriptHash>
153+
# even though this hash is of a P2PK script which is better used as bare P2WPKH, it should not matter
154+
# for the purpose of this test.
155+
rpc_result = self.nodes[0].decodescript('0020' + p2wsh_p2pk_script_hash)
156+
assert_equal('0 ' + p2wsh_p2pk_script_hash, rpc_result['asm'])
157+
# segwit scripts do not work nested into each other.
158+
# a nested segwit script should not be returned in the results.
159+
assert 'segwit' not in rpc_result
106160

107161
def decoderawtransaction_asm_sighashtype(self):
108162
"""Test decoding scripts via RPC command "decoderawtransaction".

0 commit comments

Comments
 (0)