Page MenuHomePhabricator

D4871.id15716.diff
No OneTemporary

D4871.id15716.diff

diff --git a/src/Makefile.test.include b/src/Makefile.test.include
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -87,6 +87,7 @@
test/multisig_tests.cpp \
test/net_tests.cpp \
test/netbase_tests.cpp \
+ test/op_reversebytes_tests.cpp \
test/pmt_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1235,6 +1235,21 @@
stacktop(-1) = std::move(n2);
} break;
+ case OP_REVERSEBYTES: {
+ if (!(flags & SCRIPT_ENABLE_OP_REVERSEBYTES)) {
+ return set_error(serror, ScriptError::BAD_OPCODE);
+ }
+
+ // (in -- out)
+ if (stack.size() < 1) {
+ return set_error(
+ serror, ScriptError::INVALID_STACK_OPERATION);
+ }
+
+ valtype &data = stacktop(-1);
+ std::reverse(data.begin(), data.end());
+ } break;
+
//
// Conversion operations
//
diff --git a/src/script/script.h b/src/script/script.h
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -182,6 +182,9 @@
OP_CHECKDATASIG = 0xba,
OP_CHECKDATASIGVERIFY = 0xbb,
+ // additional byte string operations
+ OP_REVERSEBYTES = 0xbc,
+
// The first op_code value after all defined opcodes
FIRST_UNDEFINED_OP_VALUE,
diff --git a/src/script/script.cpp b/src/script/script.cpp
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -232,6 +232,8 @@
return "OP_CHECKDATASIG";
case OP_CHECKDATASIGVERIFY:
return "OP_CHECKDATASIGVERIFY";
+ case OP_REVERSEBYTES:
+ return "OP_REVERSEBYTES";
// expansion
case OP_NOP1:
diff --git a/src/script/script_flags.h b/src/script/script_flags.h
--- a/src/script/script_flags.h
+++ b/src/script/script_flags.h
@@ -111,6 +111,9 @@
// Note: The Segwit Recovery feature is a (currently moot) exception to
// VERIFY_INPUT_SIGCHECKS
SCRIPT_VERIFY_INPUT_SIGCHECKS = (1U << 22),
+
+ // Whether the new OP_REVERSEBYTES opcode can be used.
+ SCRIPT_ENABLE_OP_REVERSEBYTES = (1U << 23),
};
#endif // BITCOIN_SCRIPT_SCRIPT_FLAGS_H
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -107,6 +107,7 @@
multisig_tests.cpp
net_tests.cpp
netbase_tests.cpp
+ op_reversebytes_tests.cpp
pmt_tests.cpp
policyestimator_tests.cpp
pow_tests.cpp
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -254,8 +254,8 @@
["0", "IF CHECKDATASIG ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
["0", "IF CHECKDATASIGVERIFY ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
-["0", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "opcodes >= FIRST_UNDEFINED_OP_VALUE invalid if executed"],
-["0", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
+["0", "IF REVERSEBYTES ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
+["0", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "opcodes >= FIRST_UNDEFINED_OP_VALUE invalid if executed"],
["0", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
["0", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
["0", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
@@ -961,6 +961,40 @@
"P2SH,STRICTENC", "OK", "SPLIT, maximum length with empty string"
],
+["REVERSEBYTES"],
+["0", "REVERSEBYTES 0 EQUAL", "P2SH", "BAD_OPCODE", "REVERSEBYTES, not yet activated in executed branch"],
+["0x02 0xbeef", "REVERSEBYTES 0x02 0xefbe EQUAL", "P2SH", "BAD_OPCODE", "REVERSEBYTES, not yet activated in executed branch"],
+["", "REVERSEBYTES 0 EQUAL", "P2SH,REVERSEBYTES", "INVALID_STACK_OPERATION", "REVERSEBYTES, empty stack"],
+["0", "REVERSEBYTES 0 EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, empty data"],
+["0x01 0x99", "REVERSEBYTES 0x01 0x99 EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 1 byte"],
+["0x02 0xbeef", "REVERSEBYTES 0x02 0xefbe EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 2 bytes"],
+["0x03 0xdeada1", "REVERSEBYTES 0x03 0xa1adde EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 3 bytes"],
+["0x04 0xdeadbeef", "REVERSEBYTES 0x04 0xefbeadde EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 4 bytes"],
+["'Bitcoin:_A_peer-to-peer_electronic_cash_system'", "REVERSEBYTES 'metsys_hsac_cinortcele_reep-ot-reep_A_:nioctiB' EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, whitepaper title"],
+[
+ "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'",
+ "REVERSEBYTES 'hhfanqyfcglncrriufjfvjgqcbbwrilaxeczpotsfzvmyrwlobpjtvtmhamvzoiwwjqazpyekvxpvsiiwuvertiqqiksmgkfplzamdvcelrrjsrvsxgojxngirwcoxhtmvtpzdjwzsuamlpudecziyiqkuukdbakbswcyelaksmudkgqccxpbobrkdpajsgogcuspzrptvrrsyuhrtxcftlpwlodgkavcqwofyxcwrsbovwojgznkiyijhohlyxevhqnqsibheuinifzybtbblzutjtsrotyqndybrncsdyrzlblxctejeyienvjeuslnzxomqzclruxceqispbntxngdpttfzzvqxshwlktvtkupouvjaugatahxgzzjidcauprbwdwakushoyhmogialwljyjrluudbkwjrkitxfsbtrvvyaesdosyzyxiysldhrybsxfrzpwwpubzncyzupdwoumfkfxnivdsryzlizjumarmlpqwfwqvngfegryrinviygnz' EQUAL",
+ "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 520 bytes"
+],
+["0x03 0x123456", "REVERSEBYTES 0x03 0x563412 OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 3 bytes equal"],
+["0x06 0x020406080a0c", "DUP REVERSEBYTES REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, 3 bytes double reverse equal"],
+["'Bitcoin:_A_peer-to-peer_electronic_cash_system'", "DUP REVERSEBYTES REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, whitepaper title double reverse equal"],
+[
+ "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'",
+ "DUP REVERSEBYTES REVERSEBYTES OP_EQUAL",
+ "P2SH,REVERSEBYTES",
+ "OK",
+ "REVERSEBYTES, 520 bytes double reverse equal"
+],
+["0x05 0x0102030201", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 1"],
+["0x08 0x7766554444556677", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 2"],
+["'madam'", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 3"],
+["'racecar'", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 4"],
+["'redrum_siris_murder'", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 5"],
+["'step_on_no_pets'", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "OK", "REVERSEBYTES, palindrome 6"],
+["'Bitcoin:_A_peer-to-peer_electronic_cash_system'", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "EVAL_FALSE", "REVERSEBYTES, non-palindrome 1"],
+["0x02 0x1234", "DUP REVERSEBYTES OP_EQUAL", "P2SH,REVERSEBYTES", "EVAL_FALSE", "REVERSEBYTES, non-palindrome 2"],
+
["NUM2BIN"],
["", "NUM2BIN 0 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "NUM2BIN, empty stack"],
["0", "NUM2BIN 0 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "NUM2BIN, one parameter"],
@@ -1241,7 +1275,7 @@
"P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in redeemScript"],
["0x50","1", "P2SH,STRICTENC", "BAD_OPCODE", "opcode 0x50 is reserved"],
-["1", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "opcodes >= FIRST_UNDEFINED_OP_VALUE invalid if executed"],
+["1", "IF REVERSEBYTES ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "opcodes >= OP_REVERSEBYTES invalid if executed normally"],
["1", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
@@ -1362,7 +1396,7 @@
["1","RESERVED", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED is reserved"],
["1","RESERVED1", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED1 is reserved"],
["1","RESERVED2", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED2 is reserved"],
-["1","0xbc", "P2SH,STRICTENC", "BAD_OPCODE", "0xbc == FIRST_UNDEFINED_OP_VALUE"],
+["1","0xbd", "P2SH,STRICTENC", "BAD_OPCODE", "0xbd == FIRST_UNDEFINED_OP_VALUE"],
["2147483648", "1ADD 1", "P2SH,STRICTENC", "UNKNOWN_ERROR", "We cannot do math on 5-byte integers"],
["2147483648", "NEGATE 1", "P2SH,STRICTENC", "UNKNOWN_ERROR", "We cannot do math on 5-byte integers"],
diff --git a/src/test/op_reversebytes_tests.cpp b/src/test/op_reversebytes_tests.cpp
new file mode 100644
--- /dev/null
+++ b/src/test/op_reversebytes_tests.cpp
@@ -0,0 +1,156 @@
+// 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,
+ {});
+}
+
+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}},
+ });
+
+ // Palindrome tests, they are their own reverse.
+ std::vector<valtype> palindromes;
+ palindromes.reserve(MAX_SCRIPT_ELEMENT_SIZE);
+
+ // Generated tests:
+ // - for iota(n) mod 256, n = 0,..,520.
+ // - for random bitstrings, n = 0,..,520.
+ // - for palindromes 0,..,n,..,0.
+ for (size_t datasize = 0; datasize <= MAX_SCRIPT_ELEMENT_SIZE; ++datasize) {
+ valtype iota_data, random_data, palindrome;
+ iota_data.reserve(datasize);
+ random_data.reserve(datasize);
+ palindrome.reserve(datasize);
+ for (size_t item = 0; item < datasize; ++item) {
+ 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(
+ {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++) {
+ // Generate random flags.
+ uint32_t flags = lcg.next();
+
+ // Empty stack.
+ CheckErrorIfEnabled(flags, {}, CScript() << OP_REVERSEBYTES,
+ ScriptError::INVALID_STACK_OPERATION);
+
+ for (const ReverseTestCase &test_case : test_cases) {
+ CheckPassReverse(flags, test_case);
+ }
+
+ for (const valtype &palindrome : palindromes) {
+ // Verify palindrome.
+ CheckPassIfEnabled(
+ flags, {palindrome},
+ CScript() << OP_DUP << OP_REVERSEBYTES << OP_EQUALVERIFY, {});
+ }
+
+ // 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()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -185,7 +185,7 @@
// anything about what happens when they are flipped. Keep them as-is.
extra_flags &=
~(SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_ENABLE_REPLAY_PROTECTION |
- SCRIPT_ENABLE_SCHNORR_MULTISIG);
+ SCRIPT_ENABLE_SCHNORR_MULTISIG | SCRIPT_ENABLE_OP_REVERSEBYTES);
uint32_t combined_flags =
expect ? (flags & ~extra_flags) : (flags | extra_flags);
// Weed out invalid flag combinations.
diff --git a/src/test/scriptflags.cpp b/src/test/scriptflags.cpp
--- a/src/test/scriptflags.cpp
+++ b/src/test/scriptflags.cpp
@@ -34,6 +34,7 @@
{"DISALLOW_SEGWIT_RECOVERY", SCRIPT_DISALLOW_SEGWIT_RECOVERY},
{"SCHNORR_MULTISIG", SCRIPT_ENABLE_SCHNORR_MULTISIG},
{"INPUT_SIGCHECKS", SCRIPT_VERIFY_INPUT_SIGCHECKS},
+ {"REVERSEBYTES", SCRIPT_ENABLE_OP_REVERSEBYTES},
};
uint32_t ParseScriptFlags(std::string strFlags) {
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -242,6 +242,9 @@
OP_CHECKDATASIG = CScriptOp(0xba)
OP_CHECKDATASIGVERIFY = CScriptOp(0xbb)
+# additional byte string operations
+OP_REVERSEBYTES = CScriptOp(0xbc)
+
# multi-byte opcodes
OP_PREFIX_BEGIN = CScriptOp(0xf0)
OP_PREFIX_END = CScriptOp(0xf7)

File Metadata

Mime Type
text/plain
Expires
Tue, May 20, 21:54 (10 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866005
Default Alt Text
D4871.id15716.diff (16 KB)

Event Timeline