Skip to content

Commit 2bc52f1

Browse files
committed
Merge pull request #3025 from sipa/noncanpush
Make signatures with non-canonical data pushes non-standard.
2 parents d5fa3ef + 87fe71e commit 2bc52f1

File tree

4 files changed

+72
-18
lines changed

4 files changed

+72
-18
lines changed

src/main.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
442442
reason = "scriptsig-not-pushonly";
443443
return false;
444444
}
445+
if (!txin.scriptSig.HasCanonicalPushes()) {
446+
reason = "non-canonical-push";
447+
return false;
448+
}
445449
}
446450

447451
unsigned int nDataOut = 0;

src/script.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,51 @@ bool CScript::IsPayToScriptHash() const
18631863
this->at(22) == OP_EQUAL);
18641864
}
18651865

1866+
bool CScript::IsPushOnly() const
1867+
{
1868+
const_iterator pc = begin();
1869+
while (pc < end())
1870+
{
1871+
opcodetype opcode;
1872+
if (!GetOp(pc, opcode))
1873+
return false;
1874+
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
1875+
// push-type opcode, however execution of OP_RESERVED fails, so
1876+
// it's not relevant to P2SH as the scriptSig would fail prior to
1877+
// the P2SH special validation code being executed.
1878+
if (opcode > OP_16)
1879+
return false;
1880+
}
1881+
return true;
1882+
}
1883+
1884+
bool CScript::HasCanonicalPushes() const
1885+
{
1886+
const_iterator pc = begin();
1887+
while (pc < end())
1888+
{
1889+
opcodetype opcode;
1890+
std::vector<unsigned char> data;
1891+
if (!GetOp(pc, opcode, data))
1892+
return false;
1893+
if (opcode > OP_16)
1894+
continue;
1895+
if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16))
1896+
// Could have used an OP_n code, rather than a 1-byte push.
1897+
return false;
1898+
if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1)
1899+
// Could have used a normal n-byte push, rather than OP_PUSHDATA1.
1900+
return false;
1901+
if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF)
1902+
// Could have used an OP_PUSHDATA1.
1903+
return false;
1904+
if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF)
1905+
// Could have used an OP_PUSHDATA2.
1906+
return false;
1907+
}
1908+
return true;
1909+
}
1910+
18661911
class CScriptVisitor : public boost::static_visitor<bool>
18671912
{
18681913
private:

src/script.h

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -541,24 +541,11 @@ class CScript : public std::vector<unsigned char>
541541

542542
bool IsPayToScriptHash() const;
543543

544-
// Called by IsStandardTx
545-
bool IsPushOnly() const
546-
{
547-
const_iterator pc = begin();
548-
while (pc < end())
549-
{
550-
opcodetype opcode;
551-
if (!GetOp(pc, opcode))
552-
return false;
553-
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
554-
// push-type opcode, however execution of OP_RESERVED fails, so
555-
// it's not relevant to P2SH as the scriptSig would fail prior to
556-
// the P2SH special validation code being executed.
557-
if (opcode > OP_16)
558-
return false;
559-
}
560-
return true;
561-
}
544+
// Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical).
545+
bool IsPushOnly() const;
546+
547+
// Called by IsStandardTx.
548+
bool HasCanonicalPushes() const;
562549

563550
// Returns whether the script is guaranteed to fail at execution,
564551
// regardless of the initial stack. This allows outputs to be pruned

src/test/script_tests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,4 +438,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
438438
BOOST_CHECK(combined == partial3c);
439439
}
440440

441+
BOOST_AUTO_TEST_CASE(script_standard_push)
442+
{
443+
for (int i=0; i<1000; i++) {
444+
CScript script;
445+
script << i;
446+
BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push.");
447+
BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Number " << i << " push is not canonical.");
448+
}
449+
450+
for (int i=0; i<1000; i++) {
451+
std::vector<unsigned char> data(i, '\111');
452+
CScript script;
453+
script << data;
454+
BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push.");
455+
BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Length " << i << " push is not canonical.");
456+
}
457+
}
458+
441459
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)