diff --git a/src/script/standard.cpp b/src/script/standard.cpp --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -82,6 +82,11 @@ } required = CScript::DecodeOP_N(opcode); while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { + static_assert(CPubKey::PUBLIC_KEY_SIZE < OP_PUSHDATA1); + static_assert(CPubKey::COMPRESSED_PUBLIC_KEY_SIZE < OP_PUSHDATA1); + if (opcode != data.size()) { + return false; + } pubkeys.emplace_back(std::move(data)); } if (!IsSmallInteger(opcode)) { diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -109,6 +109,45 @@ BOOST_CHECK(!Solver(s, whichType, solutions)); BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); + + // Try some non-minimal forms of various standard scripts + s.clear(); + s << OP_PUSHDATA1 << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + s.clear(); + s << OP_DUP << OP_HASH160 << OP_PUSHDATA1 + << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + s.clear(); + s << OP_HASH160 << OP_PUSHDATA1 << ToByteVector(CScriptID(redeemScript)) + << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + s.clear(); + s << OP_1 << OP_PUSHDATA1 << ToByteVector(pubkeys[0]) + << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + // Non-minimal pushes in OP_RETURN scripts are standard (some OP_RETURN + // protocols like SLP rely on this). Also it turns out OP_RESERVED gets past + // IsPushOnly and thus is standard here. + std::vector op_return_nonminimal{ + OP_RETURN, OP_RESERVED, OP_PUSHDATA1, 0x00, 0x01, 0x01, + OP_PUSHDATA4, 0x01, 0x00, 0x00, 0x00, 0xaa}; + s.assign(op_return_nonminimal.begin(), op_return_nonminimal.end()); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(solutions.size(), 0); } BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) {