Skip to content

Commit 3c8e3a6

Browse files
committed
Strip name prefix for IsWitnessProgram.
When the segwit-code was originally integrated with Namecoin, we did not update CScript::IsWitnessProgram to be aware of name prefixes. This has the effect that names sent to a pure segwit script (e.g. a bech32 address) can be updated by anyone even after segwit is activated for Namecoin. P2SH-Segwit addresses are working already as expected. In this change, we fix that bug. With the fix, IsWitnessProgram strips a name prefix if it exists before extracting the witness program, so that pure segwit scripts can safely hold not only coins but also names after segwit activates. Note that this is in theory a consensus change, but it can be safely committed to Namecoin as segwit is not yet activated and thus such scripts are considered anyone-can-spend with and without this change at the moment. This also includes new regression and unit tests for names and segwit, including (but not limited to) those suggested by Jeremy in namecoin/namecoin-core#258.
1 parent 729d587 commit 3c8e3a6

File tree

11 files changed

+282
-35
lines changed

11 files changed

+282
-35
lines changed

src/policy/policy.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
3838
int witnessversion = 0;
3939
std::vector<unsigned char> witnessprogram;
4040

41-
if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
41+
if (txout.scriptPubKey.IsWitnessProgram(true, witnessversion, witnessprogram)) {
4242
// sum the sizes of the parts of a transaction input
4343
// with 75% segwit discount applied to the script size.
4444
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
@@ -222,7 +222,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
222222
std::vector<unsigned char> witnessprogram;
223223

224224
// Non-witness program must not be associated with any witness
225-
if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram))
225+
if (!prevScript.IsWitnessProgram(true, witnessversion, witnessprogram))
226226
return false;
227227

228228
// Check P2WSH standard limits

src/qt/coincontroldialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
463463
CTxDestination address;
464464
int witnessversion = 0;
465465
std::vector<unsigned char> witnessprogram;
466-
if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
466+
if (out.txout.scriptPubKey.IsWitnessProgram(true, witnessversion, witnessprogram))
467467
{
468468
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
469469
fWitness = true;

src/script/interpreter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
14991499
int witnessversion;
15001500
std::vector<unsigned char> witnessprogram;
15011501
if (flags & SCRIPT_VERIFY_WITNESS) {
1502-
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
1502+
if (scriptPubKey.IsWitnessProgram(true, witnessversion, witnessprogram)) {
15031503
hadWitness = true;
15041504
if (scriptSig.size() != 0) {
15051505
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
@@ -1543,7 +1543,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
15431543

15441544
// P2SH witness program
15451545
if (flags & SCRIPT_VERIFY_WITNESS) {
1546-
if (pubKey2.IsWitnessProgram(witnessversion, witnessprogram)) {
1546+
if (pubKey2.IsWitnessProgram(true, witnessversion, witnessprogram)) {
15471547
hadWitness = true;
15481548
if (scriptSig != CScript() << std::vector<unsigned char>(pubKey2.begin(), pubKey2.end())) {
15491549
// The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we
@@ -1613,7 +1613,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
16131613

16141614
int witnessversion;
16151615
std::vector<unsigned char> witnessprogram;
1616-
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
1616+
if (scriptPubKey.IsWitnessProgram(true, witnessversion, witnessprogram)) {
16171617
return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
16181618
}
16191619

@@ -1625,7 +1625,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
16251625
scriptSig.GetOp(pc, opcode, data);
16261626
}
16271627
CScript subscript(data.begin(), data.end());
1628-
if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) {
1628+
if (subscript.IsWitnessProgram(true, witnessversion, witnessprogram)) {
16291629
return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty);
16301630
}
16311631
}

src/script/script.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,16 @@ bool CScript::IsPayToWitnessScriptHash(bool allowNames) const
224224

225225
// A witness program is any valid CScript that consists of a 1-byte push opcode
226226
// followed by a data push between 2 and 40 bytes.
227-
bool CScript::IsWitnessProgram(int& version, std::vector<unsigned char>& program) const
227+
bool CScript::IsWitnessProgram(const bool allowNames, int& version, std::vector<unsigned char>& program) const
228228
{
229+
// Strip off a name prefix if present.
230+
if (allowNames)
231+
{
232+
const CNameScript nameOp(*this);
233+
return nameOp.getAddress().IsWitnessProgram(false, version, program);
234+
}
235+
236+
// Handle the case without name prefix.
229237
if (this->size() < 4 || this->size() > 42) {
230238
return false;
231239
}

src/script/script.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ class CScript : public CScriptBase
548548
*/
549549
bool IsPayToScriptHash(bool allowNames) const;
550550
bool IsPayToWitnessScriptHash(bool allowNames) const;
551-
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;
551+
bool IsWitnessProgram(bool allowNames, int& version, std::vector<unsigned char>& program) const;
552552

553553
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
554554
bool IsPushOnly(const_iterator pc) const;

src/script/standard.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
105105

106106
int witnessversion;
107107
std::vector<unsigned char> witnessprogram;
108-
if (script.IsWitnessProgram(witnessversion, witnessprogram)) {
108+
if (script.IsWitnessProgram(false, witnessversion, witnessprogram)) {
109109
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
110110
vSolutionsRet.push_back(witnessprogram);
111111
return TX_WITNESS_V0_KEYHASH;

src/test/script_segwit_tests.cpp

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <script/names.h>
56
#include <script/script.h>
67
#include <test/test_bitcoin.h>
78

@@ -14,78 +15,95 @@ BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Valid)
1415
uint256 dummy;
1516
CScript p2wsh;
1617
p2wsh << OP_0 << ToByteVector(dummy);
17-
BOOST_CHECK(p2wsh.IsPayToWitnessScriptHash());
18+
BOOST_CHECK(p2wsh.IsPayToWitnessScriptHash(false));
1819

1920
std::vector<unsigned char> bytes = {OP_0, 32};
2021
bytes.insert(bytes.end(), 32, 0);
21-
BOOST_CHECK(CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash());
22+
BOOST_CHECK(CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash(false));
2223
}
2324

2425
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Invalid_NotOp0)
2526
{
2627
uint256 dummy;
2728
CScript notp2wsh;
2829
notp2wsh << OP_1 << ToByteVector(dummy);
29-
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash ());
30+
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash (false));
3031
}
3132

3233
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Invalid_Size)
3334
{
3435
uint160 dummy;
3536
CScript notp2wsh;
3637
notp2wsh << OP_0 << ToByteVector(dummy);
37-
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash ());
38+
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash (false));
3839
}
3940

4041
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Invalid_Nop)
4142
{
4243
uint256 dummy;
4344
CScript notp2wsh;
4445
notp2wsh << OP_0 << OP_NOP << ToByteVector(dummy);
45-
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash ());
46+
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash (false));
4647
}
4748

4849
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Invalid_EmptyScript)
4950
{
5051
CScript notp2wsh;
51-
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash ());
52+
BOOST_CHECK(!notp2wsh.IsPayToWitnessScriptHash (false));
5253
}
5354

5455
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_Invalid_Pushdata)
5556
{
5657
// A script is not P2WSH if OP_PUSHDATA is used to push the hash.
5758
std::vector<unsigned char> bytes = {OP_0, OP_PUSHDATA1, 32};
5859
bytes.insert(bytes.end(), 32, 0);
59-
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash());
60+
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash(false));
6061

6162
bytes = {OP_0, OP_PUSHDATA2, 32, 0};
6263
bytes.insert(bytes.end(), 32, 0);
63-
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash());
64+
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash(false));
6465

6566
bytes = {OP_0, OP_PUSHDATA4, 32, 0, 0, 0};
6667
bytes.insert(bytes.end(), 32, 0);
67-
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash());
68+
BOOST_CHECK(!CScript(bytes.begin(), bytes.end()).IsPayToWitnessScriptHash(false));
69+
}
70+
71+
BOOST_AUTO_TEST_CASE(IsPayToWitnessScriptHash_NamePrefix)
72+
{
73+
uint256 dummy;
74+
CScript p2wsh;
75+
p2wsh << OP_0 << ToByteVector(dummy);
76+
77+
BOOST_CHECK(p2wsh.IsPayToWitnessScriptHash(true));
78+
BOOST_CHECK(p2wsh.IsPayToWitnessScriptHash(false));
79+
80+
const valtype name(10, 'a');
81+
const valtype value(20, 'b');
82+
const CScript nameP2WSH = CNameScript::buildNameUpdate(p2wsh, name, value);
83+
84+
BOOST_CHECK(nameP2WSH.IsPayToWitnessScriptHash(true));
85+
BOOST_CHECK(!nameP2WSH.IsPayToWitnessScriptHash(false));
6886
}
6987

7088
namespace {
7189

72-
bool IsExpectedWitnessProgram(const CScript& script, const int expectedVersion, const std::vector<unsigned char>& expectedProgram)
90+
bool IsExpectedWitnessProgram(const bool allowNames, const CScript& script, const int expectedVersion, const std::vector<unsigned char>& expectedProgram)
7391
{
7492
int actualVersion;
7593
std::vector<unsigned char> actualProgram;
76-
if (!script.IsWitnessProgram(actualVersion, actualProgram)) {
94+
if (!script.IsWitnessProgram(allowNames, actualVersion, actualProgram)) {
7795
return false;
7896
}
7997
BOOST_CHECK_EQUAL(actualVersion, expectedVersion);
8098
BOOST_CHECK(actualProgram == expectedProgram);
8199
return true;
82100
}
83101

84-
bool IsNoWitnessProgram(const CScript& script)
102+
bool IsNoWitnessProgram(const bool allowNames, const CScript& script)
85103
{
86104
int dummyVersion;
87105
std::vector<unsigned char> dummyProgram;
88-
return !script.IsWitnessProgram(dummyVersion, dummyProgram);
106+
return !script.IsWitnessProgram(allowNames, dummyVersion, dummyProgram);
89107
}
90108

91109
} // anonymous namespace
@@ -95,68 +113,106 @@ BOOST_AUTO_TEST_CASE(IsWitnessProgram_Valid)
95113
std::vector<unsigned char> program = {42, 18};
96114
CScript wit;
97115
wit << OP_0 << program;
98-
BOOST_CHECK(IsExpectedWitnessProgram(wit, 0, program));
116+
BOOST_CHECK(IsExpectedWitnessProgram(false, wit, 0, program));
99117

100118
wit.clear();
101119
program.resize(40);
102120
wit << OP_16 << program;
103-
BOOST_CHECK(IsExpectedWitnessProgram(wit, 16, program));
121+
BOOST_CHECK(IsExpectedWitnessProgram(false, wit, 16, program));
104122

105123
program.resize(32);
106124
std::vector<unsigned char> bytes = {OP_5, static_cast<unsigned char>(program.size())};
107125
bytes.insert(bytes.end(), program.begin(), program.end());
108-
BOOST_CHECK(IsExpectedWitnessProgram(CScript(bytes.begin(), bytes.end()), 5, program));
126+
BOOST_CHECK(IsExpectedWitnessProgram(false, CScript(bytes.begin(), bytes.end()), 5, program));
109127
}
110128

111129
BOOST_AUTO_TEST_CASE(IsWitnessProgram_Invalid_Version)
112130
{
113131
std::vector<unsigned char> program(10);
114132
CScript nowit;
115133
nowit << OP_1NEGATE << program;
116-
BOOST_CHECK(IsNoWitnessProgram(nowit));
134+
BOOST_CHECK(IsNoWitnessProgram(false, nowit));
117135
}
118136

119137
BOOST_AUTO_TEST_CASE(IsWitnessProgram_Invalid_Size)
120138
{
121139
std::vector<unsigned char> program(1);
122140
CScript nowit;
123141
nowit << OP_0 << program;
124-
BOOST_CHECK(IsNoWitnessProgram(nowit));
142+
BOOST_CHECK(IsNoWitnessProgram(false, nowit));
125143

126144
nowit.clear();
127145
program.resize(41);
128146
nowit << OP_0 << program;
129-
BOOST_CHECK(IsNoWitnessProgram(nowit));
147+
BOOST_CHECK(IsNoWitnessProgram(false, nowit));
130148
}
131149

132150
BOOST_AUTO_TEST_CASE(IsWitnessProgram_Invalid_Nop)
133151
{
134152
std::vector<unsigned char> program(10);
135153
CScript nowit;
136154
nowit << OP_0 << OP_NOP << program;
137-
BOOST_CHECK(IsNoWitnessProgram(nowit));
155+
BOOST_CHECK(IsNoWitnessProgram(false, nowit));
138156
}
139157

140158
BOOST_AUTO_TEST_CASE(IsWitnessProgram_Invalid_EmptyScript)
141159
{
142160
CScript nowit;
143-
BOOST_CHECK(IsNoWitnessProgram(nowit));
161+
BOOST_CHECK(IsNoWitnessProgram(false, nowit));
144162
}
145163

146164
BOOST_AUTO_TEST_CASE(IsWitnessProgram_Invalid_Pushdata)
147165
{
148166
// A script is no witness program if OP_PUSHDATA is used to push the hash.
149167
std::vector<unsigned char> bytes = {OP_0, OP_PUSHDATA1, 32};
150168
bytes.insert(bytes.end(), 32, 0);
151-
BOOST_CHECK(IsNoWitnessProgram(CScript(bytes.begin(), bytes.end())));
169+
BOOST_CHECK(IsNoWitnessProgram(false, CScript(bytes.begin(), bytes.end())));
152170

153171
bytes = {OP_0, OP_PUSHDATA2, 32, 0};
154172
bytes.insert(bytes.end(), 32, 0);
155-
BOOST_CHECK(IsNoWitnessProgram(CScript(bytes.begin(), bytes.end())));
173+
BOOST_CHECK(IsNoWitnessProgram(false, CScript(bytes.begin(), bytes.end())));
156174

157175
bytes = {OP_0, OP_PUSHDATA4, 32, 0, 0, 0};
158176
bytes.insert(bytes.end(), 32, 0);
159-
BOOST_CHECK(IsNoWitnessProgram(CScript(bytes.begin(), bytes.end())));
177+
BOOST_CHECK(IsNoWitnessProgram(false, CScript(bytes.begin(), bytes.end())));
178+
}
179+
180+
BOOST_AUTO_TEST_CASE(IsWitnessProgram_WithNamePrefix)
181+
{
182+
const std::vector<unsigned char> program(20, 42);
183+
CScript wit;
184+
wit << OP_0 << program;
185+
186+
BOOST_CHECK(IsExpectedWitnessProgram(true, wit, 0, program));
187+
BOOST_CHECK(IsExpectedWitnessProgram(false, wit, 0, program));
188+
189+
const valtype name(10, 'a');
190+
const valtype value(20, 'b');
191+
const CScript nameWit = CNameScript::buildNameUpdate(wit, name, value);
192+
193+
BOOST_CHECK(IsExpectedWitnessProgram(true, nameWit, 0, program));
194+
BOOST_CHECK(!IsExpectedWitnessProgram(false, nameWit, 0, program));
195+
}
196+
197+
BOOST_AUTO_TEST_CASE(IsWitnessProgram_NamePrefixNotMisinterpreted)
198+
{
199+
/* Name prefixes themselves start with OP_1 to OP_3, which is also
200+
a valid start for a witness program. Make sure that they are not
201+
misinterpreted as witness programs. */
202+
203+
const valtype name(10, 'a');
204+
const valtype value(20, 'b');
205+
const valtype rand(15, 'x');
206+
207+
const CScript nameNew = CNameScript::buildNameNew(CScript(), name, rand);
208+
const CScript nameFirst = CNameScript::buildNameFirstupdate(CScript(), name, value, rand);
209+
const CScript nameUpdate = CNameScript::buildNameUpdate(CScript(), name, value);
210+
211+
for (const auto& scr : {nameNew, nameFirst, nameUpdate})
212+
{
213+
BOOST_CHECK(IsNoWitnessProgram(true, scr));
214+
BOOST_CHECK(IsNoWitnessProgram(false, scr));
215+
}
160216
}
161217

162218
BOOST_AUTO_TEST_SUITE_END()

src/wallet/wallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2608,7 +2608,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
26082608
// Check if any destination contains a witness program:
26092609
int witnessversion = 0;
26102610
std::vector<unsigned char> witnessprogram;
2611-
if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
2611+
if (recipient.scriptPubKey.IsWitnessProgram(true, witnessversion, witnessprogram)) {
26122612
return OutputType::BECH32;
26132613
}
26142614
}

0 commit comments

Comments
 (0)