Changeset View
Changeset View
Standalone View
Standalone View
src/test/op_reversebytes_tests.cpp
- This file was added.
// Copyright (c) 2020 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/interpreter.h> | |||||
#include <script/script.h> | |||||
#include <test/lcg.h> | |||||
#include <test/test_bitcoin.h> | |||||
#include <boost/test/unit_test.hpp> | |||||
typedef std::vector<uint8_t> valtype; | |||||
typedef std::vector<valtype> stacktype; | |||||
BOOST_FIXTURE_TEST_SUITE(op_reversebytes_tests, BasicTestingSetup) | |||||
struct ReverseTestCase { | |||||
const valtype item; | |||||
const valtype reversed_item; | |||||
}; | |||||
static void CheckErrorWithFlags(const uint32_t flags, | |||||
const stacktype &original_stack, | |||||
const CScript &script, | |||||
const ScriptError expected) { | |||||
BaseSignatureChecker sigchecker; | |||||
ScriptError err = ScriptError::OK; | |||||
stacktype stack{original_stack}; | |||||
bool r = EvalScript(stack, script, flags, sigchecker, &err); | |||||
BOOST_CHECK(!r); | |||||
BOOST_CHECK(err == expected); | |||||
} | |||||
static void CheckPassWithFlags(const uint32_t flags, | |||||
const stacktype &original_stack, | |||||
const CScript &script, | |||||
const stacktype &expected) { | |||||
BaseSignatureChecker sigchecker; | |||||
ScriptError err = ScriptError::OK; | |||||
stacktype stack{original_stack}; | |||||
bool r = EvalScript(stack, script, flags, sigchecker, &err); | |||||
BOOST_CHECK(r); | |||||
BOOST_CHECK(err == ScriptError::OK); | |||||
BOOST_CHECK(stack == expected); | |||||
} | |||||
/** | |||||
* Verifies that the given error occurs with OP_REVERSEBYTES enabled | |||||
* and that BAD_OPCODE occurs if disabled. | |||||
*/ | |||||
static void CheckErrorIfEnabled(const uint32_t flags, | |||||
const stacktype &original_stack, | |||||
const CScript &script, | |||||
const ScriptError expected) { | |||||
CheckErrorWithFlags(flags | SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | |||||
script, expected); | |||||
CheckErrorWithFlags(flags & ~SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | |||||
script, ScriptError::BAD_OPCODE); | |||||
} | |||||
/** | |||||
* Verifies that the given stack results with OP_REVERSEBYTES enabled | |||||
* and that BAD_OPCODE occurs if disabled. | |||||
*/ | |||||
static void CheckPassIfEnabled(const uint32_t flags, | |||||
const stacktype &original_stack, | |||||
const CScript &script, | |||||
const stacktype &expected) { | |||||
CheckPassWithFlags(flags | SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | |||||
script, expected); | |||||
CheckErrorWithFlags(flags & ~SCRIPT_ENABLE_OP_REVERSEBYTES, original_stack, | |||||
script, ScriptError::BAD_OPCODE); | |||||
} | |||||
/** | |||||
* Verifies a given reverse test case. | |||||
* Checks both if <item> OP_REVERSEBYTES results in <reversed_item> and | |||||
* whether double-reversing <item> is a no-op. | |||||
*/ | |||||
static void CheckPassReverse(const uint32_t flags, | |||||
const ReverseTestCase &reverse_case) { | |||||
CheckPassIfEnabled(flags, {reverse_case.item}, CScript() << OP_REVERSEBYTES, | |||||
{reverse_case.reversed_item}); | |||||
CheckPassIfEnabled(flags, {reverse_case.item}, | |||||
CScript() << OP_DUP << OP_REVERSEBYTES << OP_REVERSEBYTES | |||||
<< OP_EQUALVERIFY, | |||||
{}); | |||||
deadalnixUnsubmitted Not Done Inline Actionsdeadalnix: likestamp | |||||
} | |||||
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}}, | |||||
}); | |||||
deadalnixUnsubmitted Not Done Inline Actionsdeadalnix: likestamp | |||||
// Generated tests for iota(n) mod 256, n = 0,..,520. | |||||
for (uint32_t datasize = 0; datasize <= MAX_SCRIPT_ELEMENT_SIZE; | |||||
deadalnixUnsubmitted Not Done Inline ActionsYou probably want to use size_t here deadalnix: You probably want to use size_t here | |||||
++datasize) { | |||||
valtype data; | |||||
data.reserve(datasize); | |||||
for (uint32_t item = 0; item < datasize; ++item) { | |||||
data.push_back(uint8_t(item % 256)); | |||||
deadalnixUnsubmitted Not Done Inline Actionsemplace_back and you won't need to cast deadalnix: emplace_back and you won't need to cast | |||||
} | |||||
valtype reversed_data(data.rbegin(), data.rend()); | |||||
test_cases.push_back({data, reversed_data}); | |||||
deadalnixUnsubmitted Not Done Inline ActionsNote that you are making a copy of reversed_data here and then immediately destroying it. This is not a huge deal, but not doing so is actually less code, so I'm wondering what's up. test_cases.push_back({data, {data.rbegin(), data.rend()}}); deadalnix: Note that you are making a copy of reversed_data here and then immediately destroying it. This… | |||||
tobias_ruckAuthorUnsubmitted Done Inline Actionswhat‘s going on it that I‘m used to Rust‘s move semantics ;) tobias_ruck: what‘s going on it that I‘m used to Rust‘s move semantics ;)
thanks for clarifying that | |||||
} | |||||
// Generated tests for random bitstrings, n = 0,..,520. | |||||
for (uint32_t datasize = 0; datasize <= MAX_SCRIPT_ELEMENT_SIZE; | |||||
deadalnixUnsubmitted Not Done Inline ActionsWhy not use the same outer loop as the previous one? deadalnix: Why not use the same outer loop as the previous one? | |||||
++datasize) { | |||||
valtype data; | |||||
data.reserve(datasize); | |||||
for (uint32_t item = 0; item < datasize; ++item) { | |||||
data.push_back(uint8_t(lcg.next() % 256)); | |||||
deadalnixUnsubmitted Not Done Inline Actionsdito deadalnix: dito | |||||
} | |||||
valtype reversed_data(data.rbegin(), data.rend()); | |||||
test_cases.push_back({data, reversed_data}); | |||||
} | |||||
for (int i = 0; i < 4096; i++) { | |||||
// Generate random flags. | |||||
uint32_t flags = lcg.next(); | |||||
// Empty stack. | |||||
CheckErrorIfEnabled(flags, {}, CScript() << OP_REVERSEBYTES, | |||||
ScriptError::INVALID_STACK_OPERATION); | |||||
for (auto test_case = test_cases.begin(); test_case < test_cases.end(); | |||||
++test_case) { | |||||
deadalnixUnsubmitted Not Done Inline Actionsfor(const ReverseTestCase&test_case : test_cases) { ... } deadalnix: for(const ReverseTestCase&test_case : test_cases) { ... } | |||||
CheckPassReverse(flags, *test_case); | |||||
} | |||||
// Verify palindrome. | |||||
CheckPassIfEnabled( | |||||
flags, {{0x01, 0x02, 0x03, 0x02, 0x01}}, | |||||
CScript() << OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, {}); | |||||
deadalnixUnsubmitted Not Done Inline ActionsYou probably want to run this guy over a few palindromes to check edge cases. 0, odd, even and maximum size come to mind, and do it in the flag loop. deadalnix: You probably want to run this guy over a few palindromes to check edge cases. 0, odd, even and… | |||||
tobias_ruckAuthorUnsubmitted Done Inline ActionsGo Hang a Salami. I'm a Lasagna Hog! tobias_ruck: Go Hang a Salami. I'm a Lasagna Hog! | |||||
// Verify non-palindrome fails. | |||||
CheckErrorIfEnabled(flags, {{0x01, 0x02, 0x03, 0x02, 0x02}}, | |||||
CScript() | |||||
<< OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, | |||||
ScriptError::EQUALVERIFY); | |||||
} | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() |