Changeset View
Changeset View
Standalone View
Standalone View
src/test/script_pushonly_tests.cpp
- This file was added.
// Copyright (c) 2019 The Bitcoin developers | |||||
// Distributed under the MIT software license, see the accompanying | |||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | |||||
#include <script/script.h> | |||||
#include <test/test_bitcoin.h> | |||||
#include <boost/test/unit_test.hpp> | |||||
inline CScript script_from_raw(std::vector<uint8_t> bytes) { | |||||
CScript tmp(bytes.begin(), bytes.end()); | |||||
return tmp; | |||||
} | |||||
BOOST_FIXTURE_TEST_SUITE(script_pushonly_tests, BasicTestingSetup) | |||||
BOOST_AUTO_TEST_CASE(invalid_scripts) { | |||||
// IsPushOnly returns false when given a script containing only pushes that | |||||
// are invalid due to truncation. IsPushOnly() is consensus critical because | |||||
// P2SH evaluation uses it, although this specific behavior should not be | |||||
// consensus critical as the P2SH evaluation would fail first due to the | |||||
// invalid push. Still, it doesn't hurt to test it explicitly. | |||||
{ | |||||
CScript script = script_from_raw({0x01}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
// Additional tests | |||||
{ | |||||
// PUSHDATA1 truncated before size | |||||
CScript script = script_from_raw({0x4c}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA1 truncated mid-data | |||||
CScript script = script_from_raw({0x4c, 0x02, 0x00}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA2 truncated mid-size | |||||
CScript script = script_from_raw({0x4d, 0x01}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA2 truncated mid-data | |||||
CScript script = script_from_raw({0x4d, 0x02, 0x00, 0x00}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA4 truncated mid-size | |||||
CScript script = script_from_raw({0x4e, 0x01}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA4 truncated mid-data | |||||
CScript script = script_from_raw({0x4e, 0x02, 0x00, 0x00, 0x00, 0x00}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
{ | |||||
// PUSHDATA4 with massive size (4 GB), truncated | |||||
CScript script = | |||||
script_from_raw({0x4e, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}); | |||||
BOOST_CHECK(!script.IsPushOnly()); | |||||
} | |||||
jasonbcox: Nearly all of the above cases can be rolled into a loop. This provides the following benefits… | |||||
} | |||||
BOOST_AUTO_TEST_CASE(singlebyteopcodes) { | |||||
// OP_0 and OP_1NEGATE and OP_1 through OP_16 are push-only. | |||||
CScript onebytepushes = CScript() << OP_0 << OP_1NEGATE << OP_1 << OP_2 | |||||
<< OP_3 << OP_4 << OP_5 << OP_6 << OP_7 | |||||
<< OP_8 << OP_9 << OP_10 << OP_11 << OP_12 | |||||
<< OP_13 << OP_14 << OP_15 << OP_16; | |||||
BOOST_CHECK(onebytepushes.IsPushOnly()); | |||||
// OP_0 and OP_1NEGATE through to OP_16 (incl OP_RESERVED) are "PushOnly". | |||||
CScript withreserved = onebytepushes << OP_RESERVED; | |||||
BOOST_CHECK(withreserved.IsPushOnly()); | |||||
// Do all single-byte opcodes, except OP_0. | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsWhy exclude OP_0? It seems that this would be more robust if you devised a set of all opcodes and then removed the ones you don't want to test. jasonbcox: Why exclude OP_0? It seems that this would be more robust if you devised a set of all opcodes… | |||||
for (uint8_t op = 0xff; op > OP_PUSHDATA4; op--) { | |||||
CScript script = CScript() << OP_5 << opcodetype(op) << OP_11; | |||||
BOOST_CHECK_EQUAL(script.IsPushOnly(), op <= OP_16); | |||||
} | |||||
} | |||||
BOOST_AUTO_TEST_CASE(byteforms) { | |||||
// Test various ways of pushing the same thing. | |||||
// Do 0-byte (null) push in all four forms. | |||||
{ | |||||
CScript scriptPUSH = CScript() << OP_0; | |||||
BOOST_CHECK(scriptPUSH.IsPushOnly()); | |||||
CScript scriptPUSHDATA1 = script_from_raw({0x4c, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA1.IsPushOnly()); | |||||
CScript scriptPUSHDATA2 = script_from_raw({0x4d, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 0x00, 0x00, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// Do all possible 1-byte pushes in all four forms. | |||||
for (int i = 0; i < 256; i++) { | |||||
CScript scriptPUSH = script_from_raw({0x01, uint8_t(i)}); | |||||
BOOST_CHECK(scriptPUSH.IsPushOnly()); | |||||
CScript scriptPUSHDATA1 = script_from_raw({0x4c, 0x01, uint8_t(i)}); | |||||
BOOST_CHECK(scriptPUSHDATA1.IsPushOnly()); | |||||
CScript scriptPUSHDATA2 = | |||||
script_from_raw({0x4d, 0x01, 0x00, uint8_t(i)}); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 0x01, 0x00, 0x00, 0x00, uint8_t(i)}); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// Do various longer pushes. | |||||
// two bytes: [0x00, 0x00] | |||||
{ | |||||
CScript scriptPUSH = script_from_raw({0x02, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSH.IsPushOnly()); | |||||
CScript scriptPUSHDATA1 = script_from_raw({0x4c, 0x02, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA1.IsPushOnly()); | |||||
CScript scriptPUSHDATA2 = | |||||
script_from_raw({0x4d, 0x02, 0x00, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// 75 zero bytes | |||||
{ | |||||
CScript scriptPUSH = script_from_raw({75}); | |||||
scriptPUSH.resize(scriptPUSH.size() + 75); | |||||
BOOST_CHECK(scriptPUSH.IsPushOnly()); | |||||
CScript scriptPUSHDATA1 = script_from_raw({0x4c, 75}); | |||||
scriptPUSHDATA1.resize(scriptPUSHDATA1.size() + 75); | |||||
BOOST_CHECK(scriptPUSHDATA1.IsPushOnly()); | |||||
CScript scriptPUSHDATA2 = script_from_raw({ | |||||
0x4d, | |||||
75, | |||||
0x00, | |||||
}); | |||||
scriptPUSHDATA2.resize(scriptPUSHDATA2.size() + 75); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = script_from_raw({0x4e, 75, 0x00, 0x00, 0x00}); | |||||
scriptPUSHDATA4.resize(scriptPUSHDATA4.size() + 75); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// 255 zero bytes | |||||
{ | |||||
CScript scriptPUSHDATA1 = script_from_raw({0x4c, 255}); | |||||
scriptPUSHDATA1.resize(scriptPUSHDATA1.size() + 255); | |||||
BOOST_CHECK(scriptPUSHDATA1.IsPushOnly()); | |||||
CScript scriptPUSHDATA2 = script_from_raw({ | |||||
0x4d, | |||||
255, | |||||
0x00, | |||||
}); | |||||
scriptPUSHDATA2.resize(scriptPUSHDATA2.size() + 255); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 255, 0x00, 0x00, 0x00}); | |||||
scriptPUSHDATA4.resize(scriptPUSHDATA4.size() + 255); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// 0xffff zero bytes | |||||
{ | |||||
CScript scriptPUSHDATA2 = script_from_raw({ | |||||
0x4d, | |||||
0xff, | |||||
0xff, | |||||
}); | |||||
scriptPUSHDATA2.resize(scriptPUSHDATA2.size() + 0xffff); | |||||
BOOST_CHECK(scriptPUSHDATA2.IsPushOnly()); | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 0xff, 0xff, 0x00, 0x00}); | |||||
scriptPUSHDATA4.resize(scriptPUSHDATA4.size() + 0xffff); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
// 0x10000 zero bytes | |||||
{ | |||||
CScript scriptPUSHDATA4 = | |||||
script_from_raw({0x4e, 0x00, 0x00, 0x01, 0x00}); | |||||
scriptPUSHDATA4.resize(scriptPUSHDATA4.size() + 0x10000); | |||||
BOOST_CHECK(scriptPUSHDATA4.IsPushOnly()); | |||||
} | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() |
Nearly all of the above cases can be rolled into a loop. This provides the following benefits: