Page MenuHomePhabricator

D1220.diff
No OneTemporary

D1220.diff

diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -295,7 +295,6 @@
switch (opcode) {
case OP_CAT:
case OP_SPLIT:
- case OP_BIN2NUM:
case OP_NUM2BIN:
case OP_INVERT:
case OP_2MUL:
@@ -311,6 +310,7 @@
case OP_AND:
case OP_OR:
case OP_XOR:
+ case OP_BIN2NUM:
// Opcodes that have been reenabled.
if ((flags & SCRIPT_ENABLE_MONOLITH_OPCODES) == 0) {
return true;
@@ -858,6 +858,7 @@
}
valtype &vch1 = stacktop(-2);
valtype &vch2 = stacktop(-1);
+
bool fEqual = (vch1 == vch2);
// OP_NOTEQUAL is disabled because it would be too
// easy to say something like n != 1 and have some
@@ -1246,6 +1247,26 @@
}
} break;
+ //
+ // Conversion operations
+ //
+ case OP_BIN2NUM: {
+ // (in -- out)
+ if (stack.size() < 1) {
+ return set_error(
+ serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ }
+
+ valtype &n = stacktop(-1);
+ CScriptNum::MinimallyEncode(n);
+
+ // The resulting number must be a valid number.
+ if (!CScriptNum::IsMinimallyEncoded(n)) {
+ return set_error(serror,
+ SCRIPT_ERR_INVALID_NUMBER_RANGE);
+ }
+ } break;
+
default:
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
diff --git a/src/script/script_error.h b/src/script/script_error.h
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -22,6 +22,7 @@
/* Operands checks */
SCRIPT_ERR_INVALID_OPERAND_SIZE,
+ SCRIPT_ERR_INVALID_NUMBER_RANGE,
/* Failed verify operations */
SCRIPT_ERR_VERIFY,
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -36,6 +36,9 @@
return "Pubkey count negative or limit exceeded";
case SCRIPT_ERR_INVALID_OPERAND_SIZE:
return "Invalid operand size";
+ case SCRIPT_ERR_INVALID_NUMBER_RANGE:
+ return "Given operand is not a number within the valid range "
+ "[-2^31...2^31]";
case SCRIPT_ERR_BAD_OPCODE:
return "Opcode missing or not understood";
case SCRIPT_ERR_DISABLED_OPCODE:
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
@@ -828,7 +828,33 @@
["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "NUM2BIN disabled"],
["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "NUM2BIN disabled"],
["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "BIN2NUM disabled"],
-["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "BIN2NUM disabled"],
+["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM enabled"],
+
+["BIN2NUM"],
+["", "BIN2NUM 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "BIN2NUM, empty stack"],
+["0", "BIN2NUM 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, canonical arguement"],
+["1", "BIN2NUM 1 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, canonical arguement"],
+["-42", "BIN2NUM -42 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, canonical arguement"],
+["0x01 0x00", "BIN2NUM 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, non-canonical arguement"],
+["0x04 0xffffff7f", "BIN2NUM 2147483647 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, maximum size arguement"],
+["0x04 0xffffffff", "BIN2NUM -2147483647 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, maximum size arguement"],
+["0x05 0xffffffff00", "BIN2NUM 2147483647 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_NUMBER_RANGE", "BIN2NUM, oversized arguement"],
+["0x05 0xffffff7f80", "BIN2NUM -2147483647 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "BIN2NUM, non-canonical maximum size arguement"],
+["0x05 0x0100000000", "BIN2NUM 1 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"],
+["0x05 0xFE00000000", "BIN2NUM 254 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"],
+["0x05 0x0500000080", "BIN2NUM 0x01 0x85 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK"],
+["0x03 0x800000", "BIN2NUM 128 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Pad where MSB of number is set"],
+["0x03 0x800080", "BIN2NUM -128 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Pad where MSB of number is set"],
+["0x02 0x8000", "BIN2NUM 128 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Pad where MSB of number is set"],
+["0x02 0x8080", "BIN2NUM -128 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Pad where MSB of number is set"],
+["0x03 0x0f0000", "BIN2NUM 15 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Don't pad where MSB of number is not set"],
+["0x03 0x0f0080", "BIN2NUM -15 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Don't pad where MSB of number is not set"],
+["0x02 0x0f00", "BIN2NUM 15 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Don't pad where MSB of number is not set"],
+["0x02 0x0f80", "BIN2NUM -15 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Don't pad where MSB of number is not set"],
+["0x05 0x0100800000", "BIN2NUM 8388609 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Ensure significant zero bytes are retained"],
+["0x05 0x0100800080", "BIN2NUM -8388609 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Ensure significant zero bytes are retained"],
+["0x05 0x01000f0000", "BIN2NUM 983041 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Ensure significant zero bytes are retained"],
+["0x05 0x01000f0080", "BIN2NUM -983041 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Ensure significant zero bytes are retained"],
["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
diff --git a/src/test/monolith_opcodes.cpp b/src/test/monolith_opcodes.cpp
--- a/src/test/monolith_opcodes.cpp
+++ b/src/test/monolith_opcodes.cpp
@@ -352,4 +352,95 @@
CheckAllBitwiseOpErrors({b, {}}, SCRIPT_ERR_INVALID_OPERAND_SIZE);
}
+static void CheckBin2NumOp(const valtype &n, const valtype &expected) {
+ BaseSignatureChecker sigchecker;
+
+ for (uint32_t flags : flagset) {
+ ScriptError err = SCRIPT_ERR_OK;
+ std::vector<valtype> stack{n};
+ bool r = EvalScript(stack, CScript() << OP_BIN2NUM,
+ flags | SCRIPT_ENABLE_MONOLITH_OPCODES, sigchecker,
+ &err);
+ BOOST_CHECK(r);
+
+ std::vector<valtype> expected_stack{expected};
+ BOOST_CHECK(stack == expected_stack);
+
+ // Make sure that if we do not pass the monolith flag, opcodes are still
+ // disabled.
+ err = SCRIPT_ERR_OK;
+ stack = {n};
+ r = EvalScript(stack, CScript() << OP_BIN2NUM, flags, sigchecker, &err);
+ BOOST_CHECK(!r);
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_DISABLED_OPCODE);
+
+ // TODO: Check roundtrip with NUM2BIN when NUM2BIN is implemented.
+ }
+}
+
+static void CheckBin2NumError(const std::vector<valtype> &original_stack,
+ ScriptError expected_error) {
+ BaseSignatureChecker sigchecker;
+
+ for (uint32_t flags : flagset) {
+ ScriptError err = SCRIPT_ERR_OK;
+ std::vector<valtype> stack{original_stack};
+ bool r = EvalScript(stack, CScript() << OP_BIN2NUM,
+ flags | SCRIPT_ENABLE_MONOLITH_OPCODES, sigchecker,
+ &err);
+ BOOST_CHECK(!r);
+ BOOST_CHECK_EQUAL(err, expected_error);
+
+ // Make sure that if we do not pass the monolith flag, opcodes are still
+ // disabled.
+ err = SCRIPT_ERR_OK;
+ stack = {original_stack};
+ r = EvalScript(stack, CScript() << OP_BIN2NUM, flags, sigchecker, &err);
+ BOOST_CHECK(!r);
+ BOOST_CHECK_EQUAL(err, SCRIPT_ERR_DISABLED_OPCODE);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(type_conversion_test) {
+ valtype empty;
+ CheckBin2NumOp(empty, empty);
+
+ valtype paddedzero, paddednegzero;
+ for (size_t i = 0; i <= MAX_SCRIPT_ELEMENT_SIZE; i++) {
+ CheckBin2NumOp(paddedzero, empty);
+ paddedzero.push_back(0x00);
+
+ paddednegzero.push_back(0x80);
+ CheckBin2NumOp(paddednegzero, empty);
+ paddednegzero[paddednegzero.size() - 1] = 0x00;
+ }
+
+ // Merge leading byte when sign bit isn't used.
+ std::vector<uint8_t> k{0x7f}, negk{0xff};
+ std::vector<uint8_t> kpadded = k, negkpadded = negk;
+ for (size_t i = 0; i < MAX_SCRIPT_ELEMENT_SIZE; i++) {
+ CheckBin2NumOp(kpadded, k);
+ kpadded.push_back(0x00);
+
+ CheckBin2NumOp(negkpadded, negk);
+ negkpadded[negkpadded.size() - 1] &= 0x7f;
+ negkpadded.push_back(0x80);
+ }
+
+ // Some known values.
+ CheckBin2NumOp({0xab, 0xcd, 0xef, 0x00}, {0xab, 0xcd, 0xef, 0x00});
+ CheckBin2NumOp({0xab, 0xcd, 0x7f, 0x00}, {0xab, 0xcd, 0x7f});
+
+ // Reductions
+ CheckBin2NumOp({0xab, 0xcd, 0xef, 0x42, 0x80}, {0xab, 0xcd, 0xef, 0xc2});
+ CheckBin2NumOp({0xab, 0xcd, 0x7f, 0x42, 0x00}, {0xab, 0xcd, 0x7f, 0x42});
+
+ // Empty stack is an error.
+ CheckBin2NumError({}, SCRIPT_ERR_INVALID_STACK_OPERATION);
+ CheckBin2NumError({{0xab, 0xcd, 0xef, 0xc2, 0x80}},
+ SCRIPT_ERR_INVALID_NUMBER_RANGE);
+ CheckBin2NumError({{0x00, 0x00, 0x00, 0x80, 0x80}},
+ SCRIPT_ERR_INVALID_NUMBER_RANGE);
+}
+
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
@@ -63,6 +63,7 @@
{SCRIPT_ERR_SIG_COUNT, "SIG_COUNT"},
{SCRIPT_ERR_PUBKEY_COUNT, "PUBKEY_COUNT"},
{SCRIPT_ERR_INVALID_OPERAND_SIZE, "OPERAND_SIZE"},
+ {SCRIPT_ERR_INVALID_NUMBER_RANGE, "INVALID_NUMBER_RANGE"},
{SCRIPT_ERR_VERIFY, "VERIFY"},
{SCRIPT_ERR_EQUALVERIFY, "EQUALVERIFY"},
{SCRIPT_ERR_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY"},

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 1, 11:53 (3 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187711
Default Alt Text
D1220.diff (10 KB)

Event Timeline