Changeset View
Changeset View
Standalone View
Standalone View
src/test/op_reversebytes_tests.cpp
// Copyright (c) 2020 The Bitcoin developers | // Copyright (c) 2020 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <policy/policy.h> | |||||
#include <script/interpreter.h> | #include <script/interpreter.h> | ||||
#include <script/script.h> | #include <script/script.h> | ||||
#include <test/lcg.h> | #include <test/lcg.h> | ||||
#include <test/setup_common.h> | #include <test/setup_common.h> | ||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
typedef std::vector<uint8_t> valtype; | typedef std::vector<uint8_t> valtype; | ||||
typedef std::vector<valtype> stacktype; | typedef std::vector<valtype> stacktype; | ||||
static std::set<uint32_t> curated_datasizes({0, 1, 2, 10, 16, 32, 50, 128, 300, | |||||
400, 512, 519, 520}); | |||||
BOOST_FIXTURE_TEST_SUITE(op_reversebytes_tests, BasicTestingSetup) | BOOST_FIXTURE_TEST_SUITE(op_reversebytes_tests, BasicTestingSetup) | ||||
struct ReverseTestCase { | struct ReverseTestCase { | ||||
const valtype item; | const valtype item; | ||||
const valtype reversed_item; | const valtype reversed_item; | ||||
}; | }; | ||||
static void CheckErrorWithFlags(const uint32_t flags, | static void CheckErrorWithFlags(const uint32_t flags, | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | static void CheckPassIfEnabled(const uint32_t flags, | ||||
const stacktype &expected) { | const stacktype &expected) { | ||||
CheckPassWithFlags(flags | SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | CheckPassWithFlags(flags | SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | ||||
script, expected); | script, expected); | ||||
CheckErrorWithFlags(flags & ~SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | CheckErrorWithFlags(flags & ~SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | ||||
script, ScriptError::BAD_OPCODE); | script, ScriptError::BAD_OPCODE); | ||||
} | } | ||||
/** | /** | ||||
* Verifies a given reverse test case. | * Verifies the different combinations of a given test case. | ||||
* Checks both if <item> OP_REVERSEBYTES results in <reversed_item> and | * Checks if | ||||
* whether double-reversing <item> is a no-op. | * - <item> OP_REVERSEBYTES results in <reversed_item>, | ||||
* - <reversed_item> OP_REVERSEBYTES results in <item>, | |||||
* - <item> {OP_REVERSEBYTES} x 2 results in <item> and | |||||
* - <reversed_item> {OP_REVERSEBYTES} x 2 results in <reversed_item>. | |||||
*/ | |||||
static void CheckTestCaseCombos(const uint32_t flags, | |||||
deadalnix: This is not a good name. You can also drop the reverse test case thing and just pass two values. | |||||
const ReverseTestCase &test_case) { | |||||
CheckPassIfEnabled(flags, {test_case.item}, CScript() << OP_REVERSEBYTES, | |||||
{test_case.reversed_item}); | |||||
CheckPassIfEnabled(flags, {test_case.reversed_item}, | |||||
CScript() << OP_REVERSEBYTES, {test_case.item}); | |||||
CheckPassIfEnabled(flags, {test_case.item}, | |||||
CScript() << OP_REVERSEBYTES << OP_REVERSEBYTES, | |||||
{test_case.item}); | |||||
CheckPassIfEnabled(flags, {test_case.reversed_item}, | |||||
CScript() << OP_REVERSEBYTES << OP_REVERSEBYTES, | |||||
{test_case.reversed_item}); | |||||
} | |||||
/** | |||||
* Get a couple of interesting script flags. | |||||
*/ | */ | ||||
static void CheckPassReverse(const uint32_t flags, | static std::vector<uint32_t> GetCuratedFlagSet() { | ||||
const ReverseTestCase &reverse_case) { | std::vector<uint32_t> flagset({ | ||||
CheckPassIfEnabled(flags, {reverse_case.item}, CScript() << OP_REVERSEBYTES, | SCRIPT_VERIFY_NONE, | ||||
{reverse_case.reversed_item}); | STANDARD_SCRIPT_VERIFY_FLAGS, | ||||
CheckPassIfEnabled(flags, {reverse_case.item}, | MANDATORY_SCRIPT_VERIFY_FLAGS, | ||||
CScript() << OP_DUP << OP_REVERSEBYTES << OP_REVERSEBYTES | |||||
<< OP_EQUALVERIFY, | |||||
{}); | |||||
} | |||||
BOOST_AUTO_TEST_CASE(op_reversebytes_tests) { | |||||
MMIXLinearCongruentialGenerator lcg; | |||||
// Manual tests. | |||||
std::vector<ReverseTestCase> test_cases({ | |||||
{{}, {}}, | |||||
{{99}, {99}}, | |||||
{{0xde, 0xad}, {0xad, 0xde}}, | |||||
{{0xde, 0xad, 0xa1}, {0xa1, 0xad, 0xde}}, | |||||
{{0xde, 0xad, 0xbe, 0xef}, {0xef, 0xbe, 0xad, 0xde}}, | |||||
{{0x12, 0x34, 0x56}, {0x56, 0x34, 0x12}}, | |||||
}); | }); | ||||
for (uint32_t flagindex = 0; flagindex < 32; ++flagindex) { | |||||
uint32_t flags = 1 << flagindex; | |||||
flagset.push_back(flags); | |||||
} | |||||
return flagset; | |||||
} | |||||
// Palindrome tests, they are their own reverse. | /** | ||||
std::vector<valtype> palindromes; | * Get a random script flags. | ||||
palindromes.reserve(MAX_SCRIPT_ELEMENT_SIZE); | */ | ||||
static std::vector<uint32_t> | |||||
// Generated tests: | GetRandomFlagSet(MMIXLinearCongruentialGenerator &lcg) { | ||||
// - for iota(n) mod 256, n = 0,..,520. | size_t num_flags = 2048; | ||||
// - for random bitstrings, n = 0,..,520. | std::vector<uint32_t> flagset; | ||||
// - for palindromes 0,..,n,..,0. | flagset.reserve(num_flags); | ||||
for (size_t datasize = 0; datasize <= MAX_SCRIPT_ELEMENT_SIZE; ++datasize) { | for (size_t i = 0; i < num_flags; i++) { | ||||
valtype iota_data, random_data, palindrome; | // Generate random flags. | ||||
uint32_t flags = lcg.next(); | |||||
flagset.push_back(flags); | |||||
} | |||||
return flagset; | |||||
} | |||||
BOOST_AUTO_TEST_CASE(op_reversebytes_manual) { | |||||
// Test a few simple manual cases. | |||||
MMIXLinearCongruentialGenerator lcg; | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
deadalnixUnsubmitted Not Done Inline ActionsDude, just do for (int i = 0; i < 4096; i++) { uint32_t flags = lcg.next(); // Do stuff... } Just stop overcomplicating everything. This is not shorter, this is not faster (in fact, this is slower). Why does this exist? Also, bonus point for the Flag*Set* being a vector XD deadalnix: Dude, just do
for (int i = 0; i < 4096; i++) {
uint32_t flags = lcg.next()… | |||||
CheckTestCaseCombos(flags, {{}, {}}); | |||||
CheckTestCaseCombos(flags, {{99}, {99}}); | |||||
CheckTestCaseCombos(flags, {{0xde, 0xad}, {0xad, 0xde}}); | |||||
CheckTestCaseCombos(flags, {{0xde, 0xad, 0xa1}, {0xa1, 0xad, 0xde}}); | |||||
CheckTestCaseCombos( | |||||
flags, {{0xde, 0xad, 0xbe, 0xef}, {0xef, 0xbe, 0xad, 0xde}}); | |||||
CheckTestCaseCombos(flags, {{0x12, 0x34, 0x56}, {0x56, 0x34, 0x12}}); | |||||
} | |||||
} | |||||
static ReverseTestCase GetIotaTestCase(uint32_t datasize) { | |||||
valtype iota_data; | |||||
iota_data.reserve(datasize); | iota_data.reserve(datasize); | ||||
random_data.reserve(datasize); | |||||
palindrome.reserve(datasize); | |||||
for (size_t item = 0; item < datasize; ++item) { | for (size_t item = 0; item < datasize; ++item) { | ||||
iota_data.emplace_back(item % 256); | iota_data.emplace_back(item % 256); | ||||
random_data.emplace_back(lcg.next() % 256); | |||||
palindrome.emplace_back( | |||||
(item < (datasize + 1) / 2 ? item : datasize - item - 1) % 256); | |||||
} | } | ||||
test_cases.push_back( | return {iota_data, {iota_data.rbegin(), iota_data.rend()}}; | ||||
{iota_data, {iota_data.rbegin(), iota_data.rend()}}); | |||||
test_cases.push_back( | |||||
{random_data, {random_data.rbegin(), random_data.rend()}}); | |||||
palindromes.push_back(palindrome); | |||||
} | } | ||||
for (int i = 0; i < 4096; i++) { | BOOST_AUTO_TEST_CASE(op_reversebytes_iota) { | ||||
// Generate random flags. | // Test byte strings 0..n (mod 256). | ||||
uint32_t flags = lcg.next(); | MMIXLinearCongruentialGenerator lcg; | ||||
const std::vector<uint32_t> curated_flagset = GetCuratedFlagSet(); | |||||
for (uint32_t datasize = 0; datasize < MAX_SCRIPT_ELEMENT_SIZE; | |||||
++datasize) { | |||||
ReverseTestCase test_case = GetIotaTestCase(datasize); | |||||
for (const uint32_t flags : curated_flagset) { | |||||
CheckTestCaseCombos(flags, test_case); | |||||
} | |||||
if (curated_datasizes.find(datasize) != curated_datasizes.end()) { | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
CheckTestCaseCombos(flags, test_case); | |||||
} | |||||
} | |||||
deadalnixUnsubmitted Not Done Inline ActionsJust create a test case with all the flag instead of woving this all over the place. deadalnix: Just create a test case with all the flag instead of woving this all over the place. | |||||
} | |||||
} | |||||
// Empty stack. | static ReverseTestCase GetRandomTestCase(MMIXLinearCongruentialGenerator &lcg, | ||||
CheckErrorIfEnabled(flags, {}, CScript() << OP_REVERSEBYTES, | uint32_t datasize) { | ||||
ScriptError::INVALID_STACK_OPERATION); | valtype random_data; | ||||
random_data.reserve(datasize); | |||||
for (size_t item = 0; item < datasize; ++item) { | |||||
random_data.emplace_back(lcg.next() % 256); | |||||
} | |||||
return {random_data, {random_data.rbegin(), random_data.rend()}}; | |||||
} | |||||
BOOST_AUTO_TEST_CASE(op_reversebytes_random) { | |||||
// Test random byte strings. | |||||
MMIXLinearCongruentialGenerator lcg; | |||||
const std::vector<uint32_t> curated_flagset = GetCuratedFlagSet(); | |||||
for (uint32_t datasize = 0; datasize < MAX_SCRIPT_ELEMENT_SIZE; | |||||
++datasize) { | |||||
ReverseTestCase test_case = GetRandomTestCase(lcg, datasize); | |||||
for (const uint32_t flags : curated_flagset) { | |||||
CheckTestCaseCombos(flags, test_case); | |||||
} | |||||
if (curated_datasizes.find(datasize) != curated_datasizes.end()) { | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
CheckTestCaseCombos(flags, test_case); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
for (const ReverseTestCase &test_case : test_cases) { | static valtype GetPalindrome(uint32_t datasize) { | ||||
CheckPassReverse(flags, test_case); | valtype palindrome; | ||||
palindrome.reserve(datasize); | |||||
for (size_t item = 0; item < datasize; ++item) { | |||||
palindrome.emplace_back( | |||||
(item < (datasize + 1) / 2 ? item : datasize - item - 1) % 256); | |||||
} | |||||
return palindrome; | |||||
} | } | ||||
for (const valtype &palindrome : palindromes) { | BOOST_AUTO_TEST_CASE(op_reversebytes_palindrome) { | ||||
// Verify palindrome. | // Test palindromes. | ||||
CheckPassIfEnabled( | MMIXLinearCongruentialGenerator lcg; | ||||
flags, {palindrome}, | const std::vector<uint32_t> curated_flagset = GetCuratedFlagSet(); | ||||
CScript() << OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, {}); | for (size_t datasize = 0; datasize <= MAX_SCRIPT_ELEMENT_SIZE; ++datasize) { | ||||
valtype palindrome = GetPalindrome(datasize); | |||||
for (const uint32_t flags : curated_flagset) { | |||||
CheckPassIfEnabled(flags, {palindrome}, | |||||
CScript() << OP_REVERSEBYTES, {palindrome}); | |||||
} | |||||
if (curated_datasizes.find(datasize) != curated_datasizes.end()) { | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
CheckPassIfEnabled(flags, {palindrome}, | |||||
CScript() << OP_REVERSEBYTES, {palindrome}); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
BOOST_AUTO_TEST_CASE(op_reversebytes_non_palindrome) { | |||||
// Verify non-palindrome fails. | // Verify non-palindrome fails. | ||||
MMIXLinearCongruentialGenerator lcg; | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
CheckErrorIfEnabled(flags, {{0x01, 0x02, 0x03, 0x02, 0x02}}, | CheckErrorIfEnabled(flags, {{0x01, 0x02, 0x03, 0x02, 0x02}}, | ||||
CScript() | CScript() | ||||
<< OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, | << OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, | ||||
ScriptError::EQUALVERIFY); | ScriptError::EQUALVERIFY); | ||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(op_reversebytes_empty_stack) { | |||||
// Test empty stack results in INVALID_STACK_OPERATION. | |||||
MMIXLinearCongruentialGenerator lcg; | |||||
for (const uint32_t flags : GetRandomFlagSet(lcg)) { | |||||
CheckErrorIfEnabled(flags, {}, CScript() << OP_REVERSEBYTES, | |||||
ScriptError::INVALID_STACK_OPERATION); | |||||
} | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |
This is not a good name. You can also drop the reverse test case thing and just pass two values.