Merge #10481: Decodehextx scripts sanity check
ac4e438
Sanity check transaction scripts in DecodeHexTx (Andrew Chow)5b75c47
Add a valid opcode sanity check to CScript (Andrew Chow) Tree-SHA512: a516e95c274c9d131123150c798cae8bed75925e8a4d59469967dd7f1d49f7f8161e26afb61024b821bd8dcdffdfd0d19b8dcad20b39b1106820326d8d56904d
This commit is contained in:
commit
9c248e39f2
4 changed files with 59 additions and 3 deletions
|
@ -88,10 +88,32 @@ CScript ParseScript(const std::string& s)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that all of the input and output scripts of a transaction contains valid opcodes
|
||||||
|
bool CheckTxScriptsSanity(const CMutableTransaction& tx)
|
||||||
|
{
|
||||||
|
// Check input scripts for non-coinbase txs
|
||||||
|
if (!CTransaction(tx).IsCoinBase()) {
|
||||||
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
|
if (!tx.vin[i].scriptSig.HasValidOps() || tx.vin[i].scriptSig.size() > MAX_SCRIPT_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check output scripts
|
||||||
|
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||||
|
if (!tx.vout[i].scriptPubKey.HasValidOps() || tx.vout[i].scriptPubKey.size() > MAX_SCRIPT_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
|
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
|
||||||
{
|
{
|
||||||
if (!IsHex(strHexTx))
|
if (!IsHex(strHexTx)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> txData(ParseHex(strHexTx));
|
std::vector<unsigned char> txData(ParseHex(strHexTx));
|
||||||
|
|
||||||
|
@ -99,7 +121,7 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTry
|
||||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
|
||||||
try {
|
try {
|
||||||
ssData >> tx;
|
ssData >> tx;
|
||||||
if (ssData.eof()) {
|
if (ssData.eof() && CheckTxScriptsSanity(tx)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,8 +133,9 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTry
|
||||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
try {
|
try {
|
||||||
ssData >> tx;
|
ssData >> tx;
|
||||||
if (!ssData.empty())
|
if (!ssData.empty()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -267,3 +267,16 @@ std::string CScriptWitness::ToString() const
|
||||||
}
|
}
|
||||||
return ret + ")";
|
return ret + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::HasValidOps() const
|
||||||
|
{
|
||||||
|
CScript::const_iterator it = begin();
|
||||||
|
while (it < end()) {
|
||||||
|
opcodetype opcode;
|
||||||
|
std::vector<unsigned char> item;
|
||||||
|
if (!GetOp(it, opcode, item) || opcode > MAX_OPCODE || item.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -190,6 +190,9 @@ enum opcodetype
|
||||||
OP_INVALIDOPCODE = 0xff,
|
OP_INVALIDOPCODE = 0xff,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Maximum value that an opcode can be
|
||||||
|
static const unsigned int MAX_OPCODE = OP_NOP10;
|
||||||
|
|
||||||
const char* GetOpName(opcodetype opcode);
|
const char* GetOpName(opcodetype opcode);
|
||||||
|
|
||||||
class scriptnum_error : public std::runtime_error
|
class scriptnum_error : public std::runtime_error
|
||||||
|
@ -630,6 +633,9 @@ public:
|
||||||
bool IsPushOnly(const_iterator pc) const;
|
bool IsPushOnly(const_iterator pc) const;
|
||||||
bool IsPushOnly() const;
|
bool IsPushOnly() const;
|
||||||
|
|
||||||
|
/** Check if the script contains valid OP_CODES */
|
||||||
|
bool HasValidOps() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
|
|
@ -1438,4 +1438,18 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
||||||
BOOST_CHECK(s == expect);
|
BOOST_CHECK(s == expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_HasValidOps)
|
||||||
|
{
|
||||||
|
// Exercise the HasValidOps functionality
|
||||||
|
CScript script;
|
||||||
|
script = ScriptFromHex("76a9141234567890abcdefa1a2a3a4a5a6a7a8a9a0aaab88ac"); // Normal script
|
||||||
|
BOOST_CHECK(script.HasValidOps());
|
||||||
|
script = ScriptFromHex("76a914ff34567890abcdefa1a2a3a4a5a6a7a8a9a0aaab88ac");
|
||||||
|
BOOST_CHECK(script.HasValidOps());
|
||||||
|
script = ScriptFromHex("ff88ac"); // Script with OP_INVALIDOPCODE explicit
|
||||||
|
BOOST_CHECK(!script.HasValidOps());
|
||||||
|
script = ScriptFromHex("88acc0"); // Script with undefined opcode
|
||||||
|
BOOST_CHECK(!script.HasValidOps());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue