Merge pull request #3025 from sipa/noncanpush
Make signatures with non-canonical data pushes non-standard.
This commit is contained in:
commit
2bc52f1c4a
4 changed files with 72 additions and 18 deletions
|
@ -442,6 +442,10 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
|
||||||
reason = "scriptsig-not-pushonly";
|
reason = "scriptsig-not-pushonly";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!txin.scriptSig.HasCanonicalPushes()) {
|
||||||
|
reason = "non-canonical-push";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int nDataOut = 0;
|
unsigned int nDataOut = 0;
|
||||||
|
|
|
@ -1863,6 +1863,51 @@ bool CScript::IsPayToScriptHash() const
|
||||||
this->at(22) == OP_EQUAL);
|
this->at(22) == OP_EQUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::IsPushOnly() const
|
||||||
|
{
|
||||||
|
const_iterator pc = begin();
|
||||||
|
while (pc < end())
|
||||||
|
{
|
||||||
|
opcodetype opcode;
|
||||||
|
if (!GetOp(pc, opcode))
|
||||||
|
return false;
|
||||||
|
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
|
||||||
|
// push-type opcode, however execution of OP_RESERVED fails, so
|
||||||
|
// it's not relevant to P2SH as the scriptSig would fail prior to
|
||||||
|
// the P2SH special validation code being executed.
|
||||||
|
if (opcode > OP_16)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CScript::HasCanonicalPushes() const
|
||||||
|
{
|
||||||
|
const_iterator pc = begin();
|
||||||
|
while (pc < end())
|
||||||
|
{
|
||||||
|
opcodetype opcode;
|
||||||
|
std::vector<unsigned char> data;
|
||||||
|
if (!GetOp(pc, opcode, data))
|
||||||
|
return false;
|
||||||
|
if (opcode > OP_16)
|
||||||
|
continue;
|
||||||
|
if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16))
|
||||||
|
// Could have used an OP_n code, rather than a 1-byte push.
|
||||||
|
return false;
|
||||||
|
if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1)
|
||||||
|
// Could have used a normal n-byte push, rather than OP_PUSHDATA1.
|
||||||
|
return false;
|
||||||
|
if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF)
|
||||||
|
// Could have used an OP_PUSHDATA1.
|
||||||
|
return false;
|
||||||
|
if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF)
|
||||||
|
// Could have used an OP_PUSHDATA2.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class CScriptVisitor : public boost::static_visitor<bool>
|
class CScriptVisitor : public boost::static_visitor<bool>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
23
src/script.h
23
src/script.h
|
@ -541,24 +541,11 @@ public:
|
||||||
|
|
||||||
bool IsPayToScriptHash() const;
|
bool IsPayToScriptHash() const;
|
||||||
|
|
||||||
// Called by IsStandardTx
|
// Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical).
|
||||||
bool IsPushOnly() const
|
bool IsPushOnly() const;
|
||||||
{
|
|
||||||
const_iterator pc = begin();
|
// Called by IsStandardTx.
|
||||||
while (pc < end())
|
bool HasCanonicalPushes() const;
|
||||||
{
|
|
||||||
opcodetype opcode;
|
|
||||||
if (!GetOp(pc, opcode))
|
|
||||||
return false;
|
|
||||||
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
|
|
||||||
// push-type opcode, however execution of OP_RESERVED fails, so
|
|
||||||
// it's not relevant to P2SH as the scriptSig would fail prior to
|
|
||||||
// the P2SH special validation code being executed.
|
|
||||||
if (opcode > OP_16)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns whether the script is guaranteed to fail at execution,
|
// Returns whether the script is guaranteed to fail at execution,
|
||||||
// regardless of the initial stack. This allows outputs to be pruned
|
// regardless of the initial stack. This allows outputs to be pruned
|
||||||
|
|
|
@ -438,4 +438,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
|
||||||
BOOST_CHECK(combined == partial3c);
|
BOOST_CHECK(combined == partial3c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_push)
|
||||||
|
{
|
||||||
|
for (int i=0; i<1000; i++) {
|
||||||
|
CScript script;
|
||||||
|
script << i;
|
||||||
|
BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push.");
|
||||||
|
BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Number " << i << " push is not canonical.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<1000; i++) {
|
||||||
|
std::vector<unsigned char> data(i, '\111');
|
||||||
|
CScript script;
|
||||||
|
script << data;
|
||||||
|
BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push.");
|
||||||
|
BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Length " << i << " push is not canonical.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Add table
Reference in a new issue