Skip to content

Commit cd9114e

Browse files
committed
Merge pull request bitcoin#5065
16d78bd Add valid invert of invalid every numeric opcode tests (Peter Todd) 2b62e17 Clearly separate PUSHDATA and numeric argument MINIMALDATA tests (Peter Todd) dfeec18 Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule (Peter Todd) 554147a Ensure MINIMALDATA invalid tests can only fail one way (Peter Todd) 6004e77 Improve CScriptNum() comment (Peter Todd) 698c6ab Add SCRIPT_VERIFY_MINIMALDATA (BIP62 rules 3 and 4) (Pieter Wuille) d752ba8 Add SCRIPT_VERIFY_SIGPUSHONLY (BIP62 rule 2) (Pieter Wuille)
2 parents 068b7f8 + 16d78bd commit cd9114e

11 files changed

+372
-63
lines changed

src/main.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -633,10 +633,6 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
633633
reason = "scriptsig-not-pushonly";
634634
return false;
635635
}
636-
if (!txin.scriptSig.HasCanonicalPushes()) {
637-
reason = "scriptsig-non-canonical-push";
638-
return false;
639-
}
640636
}
641637

642638
unsigned int nDataOut = 0;

src/script/interpreter.cpp

+44-11
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,29 @@ bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags) {
157157
return true;
158158
}
159159

160+
bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
161+
if (data.size() == 0) {
162+
// Could have used OP_0.
163+
return opcode == OP_0;
164+
} else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) {
165+
// Could have used OP_1 .. OP_16.
166+
return opcode == OP_1 + (data[0] - 1);
167+
} else if (data.size() == 1 && data[0] == 0x81) {
168+
// Could have used OP_1NEGATE.
169+
return opcode == OP_1NEGATE;
170+
} else if (data.size() <= 75) {
171+
// Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
172+
return opcode == data.size();
173+
} else if (data.size() <= 255) {
174+
// Could have used OP_PUSHDATA.
175+
return opcode == OP_PUSHDATA1;
176+
} else if (data.size() <= 65535) {
177+
// Could have used OP_PUSHDATA2.
178+
return opcode == OP_PUSHDATA2;
179+
}
180+
return true;
181+
}
182+
160183
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker)
161184
{
162185
CScript::const_iterator pc = script.begin();
@@ -169,6 +192,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
169192
if (script.size() > 10000)
170193
return false;
171194
int nOpCount = 0;
195+
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
172196

173197
try
174198
{
@@ -205,9 +229,12 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
205229
opcode == OP_RSHIFT)
206230
return false; // Disabled opcodes.
207231

208-
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4)
232+
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
233+
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
234+
return false;
235+
}
209236
stack.push_back(vchPushValue);
210-
else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
237+
} else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
211238
switch (opcode)
212239
{
213240
//
@@ -234,6 +261,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
234261
// ( -- value)
235262
CScriptNum bn((int)opcode - (int)(OP_1 - 1));
236263
stack.push_back(bn.getvch());
264+
// The result of these opcodes should always be the minimal way to push the data
265+
// they push, so no need for a CheckMinimalPush here.
237266
}
238267
break;
239268

@@ -458,7 +487,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
458487
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
459488
if (stack.size() < 2)
460489
return false;
461-
int n = CScriptNum(stacktop(-1)).getint();
490+
int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
462491
popstack(stack);
463492
if (n < 0 || n >= (int)stack.size())
464493
return false;
@@ -557,7 +586,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
557586
// (in -- out)
558587
if (stack.size() < 1)
559588
return false;
560-
CScriptNum bn(stacktop(-1));
589+
CScriptNum bn(stacktop(-1), fRequireMinimal);
561590
switch (opcode)
562591
{
563592
case OP_1ADD: bn += bnOne; break;
@@ -590,8 +619,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
590619
// (x1 x2 -- out)
591620
if (stack.size() < 2)
592621
return false;
593-
CScriptNum bn1(stacktop(-2));
594-
CScriptNum bn2(stacktop(-1));
622+
CScriptNum bn1(stacktop(-2), fRequireMinimal);
623+
CScriptNum bn2(stacktop(-1), fRequireMinimal);
595624
CScriptNum bn(0);
596625
switch (opcode)
597626
{
@@ -635,9 +664,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
635664
// (x min max -- out)
636665
if (stack.size() < 3)
637666
return false;
638-
CScriptNum bn1(stacktop(-3));
639-
CScriptNum bn2(stacktop(-2));
640-
CScriptNum bn3(stacktop(-1));
667+
CScriptNum bn1(stacktop(-3), fRequireMinimal);
668+
CScriptNum bn2(stacktop(-2), fRequireMinimal);
669+
CScriptNum bn3(stacktop(-1), fRequireMinimal);
641670
bool fValue = (bn2 <= bn1 && bn1 < bn3);
642671
popstack(stack);
643672
popstack(stack);
@@ -727,7 +756,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
727756
if ((int)stack.size() < i)
728757
return false;
729758

730-
int nKeysCount = CScriptNum(stacktop(-i)).getint();
759+
int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
731760
if (nKeysCount < 0 || nKeysCount > 20)
732761
return false;
733762
nOpCount += nKeysCount;
@@ -738,7 +767,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
738767
if ((int)stack.size() < i)
739768
return false;
740769

741-
int nSigsCount = CScriptNum(stacktop(-i)).getint();
770+
int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint();
742771
if (nSigsCount < 0 || nSigsCount > nKeysCount)
743772
return false;
744773
int isig = ++i;
@@ -980,6 +1009,10 @@ bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vec
9801009

9811010
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker)
9821011
{
1012+
if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) {
1013+
return false;
1014+
}
1015+
9831016
vector<vector<unsigned char> > stack, stackCopy;
9841017
if (!EvalScript(stack, scriptSig, flags, checker))
9851018
return false;

src/script/interpreter.h

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ enum
4646

4747
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7).
4848
SCRIPT_VERIFY_NULLDUMMY = (1U << 4),
49+
50+
// Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
51+
SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5),
52+
53+
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
54+
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
55+
// any other push causes the script to fail (BIP62 rule 3).
56+
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
57+
// (softfork safe)
58+
SCRIPT_VERIFY_MINIMALDATA = (1U << 6)
4959
};
5060

5161
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);

src/script/script.cpp

+2-29
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace {
1212
inline std::string ValueString(const std::vector<unsigned char>& vch)
1313
{
1414
if (vch.size() <= 4)
15-
return strprintf("%d", CScriptNum(vch).getint());
15+
return strprintf("%d", CScriptNum(vch, false).getint());
1616
else
1717
return HexStr(vch);
1818
}
@@ -230,41 +230,14 @@ bool CScript::IsPushOnly() const
230230
return false;
231231
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
232232
// push-type opcode, however execution of OP_RESERVED fails, so
233-
// it's not relevant to P2SH as the scriptSig would fail prior to
233+
// it's not relevant to P2SH/BIP62 as the scriptSig would fail prior to
234234
// the P2SH special validation code being executed.
235235
if (opcode > OP_16)
236236
return false;
237237
}
238238
return true;
239239
}
240240

241-
bool CScript::HasCanonicalPushes() const
242-
{
243-
const_iterator pc = begin();
244-
while (pc < end())
245-
{
246-
opcodetype opcode;
247-
std::vector<unsigned char> data;
248-
if (!GetOp(pc, opcode, data))
249-
return false;
250-
if (opcode > OP_16)
251-
continue;
252-
if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16))
253-
// Could have used an OP_n code, rather than a 1-byte push.
254-
return false;
255-
if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1)
256-
// Could have used a normal n-byte push, rather than OP_PUSHDATA1.
257-
return false;
258-
if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF)
259-
// Could have used an OP_PUSHDATA1.
260-
return false;
261-
if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF)
262-
// Could have used an OP_PUSHDATA2.
263-
return false;
264-
}
265-
return true;
266-
}
267-
268241
std::string CScript::ToString() const
269242
{
270243
std::string str;

src/script/script.h

+27-8
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,29 @@ class CScriptNum
192192
m_value = n;
193193
}
194194

195-
explicit CScriptNum(const std::vector<unsigned char>& vch)
195+
explicit CScriptNum(const std::vector<unsigned char>& vch, bool fRequireMinimal)
196196
{
197-
if (vch.size() > nMaxNumSize)
198-
throw scriptnum_error("CScriptNum(const std::vector<unsigned char>&) : overflow");
197+
if (vch.size() > nMaxNumSize) {
198+
throw scriptnum_error("script number overflow");
199+
}
200+
if (fRequireMinimal && vch.size() > 0) {
201+
// Check that the number is encoded with the minimum possible
202+
// number of bytes.
203+
//
204+
// If the most-significant-byte - excluding the sign bit - is zero
205+
// then we're not minimal. Note how this test also rejects the
206+
// negative-zero encoding, 0x80.
207+
if ((vch.back() & 0x7f) == 0) {
208+
// One exception: if there's more than one byte and the most
209+
// significant bit of the second-most-significant-byte is set
210+
// it would conflict with the sign bit. An example of this case
211+
// is +-255, which encode to 0xff00 and 0xff80 respectively.
212+
// (big-endian).
213+
if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) {
214+
throw scriptnum_error("non-minimally encoded script number");
215+
}
216+
}
217+
}
199218
m_value = set_vch(vch);
200219
}
201220

@@ -319,7 +338,6 @@ class CScriptNum
319338
int64_t m_value;
320339
};
321340

322-
323341
/** Serialized script, used inside transaction inputs and outputs */
324342
class CScript : public std::vector<unsigned char>
325343
{
@@ -330,6 +348,10 @@ class CScript : public std::vector<unsigned char>
330348
{
331349
push_back(n + (OP_1 - 1));
332350
}
351+
else if (n == 0)
352+
{
353+
push_back(OP_0);
354+
}
333355
else
334356
{
335357
*this << CScriptNum::serialize(n);
@@ -551,12 +573,9 @@ class CScript : public std::vector<unsigned char>
551573

552574
bool IsPayToScriptHash() const;
553575

554-
// Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical).
576+
// Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical).
555577
bool IsPushOnly() const;
556578

557-
// Called by IsStandardTx.
558-
bool HasCanonicalPushes() const;
559-
560579
// Returns whether the script is guaranteed to fail at execution,
561580
// regardless of the initial stack. This allows outputs to be pruned
562581
// instantly when entering the UTXO set.

src/script/standard.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
4141
// blocks and we must accept those blocks.
4242
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
4343
SCRIPT_VERIFY_STRICTENC |
44+
SCRIPT_VERIFY_MINIMALDATA |
4445
SCRIPT_VERIFY_NULLDUMMY;
4546

4647
// For convenience, standard but not mandatory verify flags.

0 commit comments

Comments
 (0)