Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864721
D4871.id15716.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Subscribers
None
D4871.id15716.diff
View Options
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
Details
Attached
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)
Attached To
D4871: Added OP_REVERSEBYTES+implementation, added (always disabled) activation flag
Event Timeline
Log In to Comment