diff --git a/src/core_read.cpp b/src/core_read.cpp --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -49,6 +49,9 @@ boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on); + size_t push_size = 0, next_push_size = 0; + size_t script_size = 0; + for (const auto &w : words) { if (w.empty()) { // Empty string, ignore. (boost::split given '' will return one @@ -56,6 +59,19 @@ continue; } + // Check that the expected number of byte where pushed. + if (push_size && (result.size() - script_size) != push_size) { + throw std::runtime_error( + "Hex number doesn't match the number of bytes being pushed"); + } + + // Update script size. + script_size = result.size(); + + // Make sure we keep track of the size of push operations. + push_size = next_push_size; + next_push_size = 0; + if (all(w, boost::algorithm::is_digit()) || (boost::algorithm::starts_with(w, "-") && all(std::string(w.begin() + 1, w.end()), @@ -78,6 +94,16 @@ // Raw hex data, inserted NOT pushed onto stack: std::vector raw = ParseHex(std::string(w.begin() + 2, w.end())); + if (push_size && raw.size() != push_size) { + throw std::runtime_error("Hex number doesn't match the " + "number of bytes being pushed"); + } + // If we have what looks like an immediate push, figure out its + // size. + if (!push_size && raw.size() == 1 && raw[0] < OP_PUSHDATA1) { + next_push_size = raw[0]; + } + result.insert(result.end(), raw.begin(), raw.end()); continue; } @@ -94,13 +120,35 @@ if (mapOpNames.count(w)) { // opcode, e.g. OP_ADD or ADD: - result << mapOpNames[w]; + opcodetype op = mapOpNames[w]; + + switch (op) { + case OP_PUSHDATA1: + next_push_size = 1; + break; + case OP_PUSHDATA2: + next_push_size = 2; + break; + case OP_PUSHDATA4: + next_push_size = 4; + break; + default: + break; + } + + result << op; continue; } throw std::runtime_error("Error parsing script: " + s); } + // Check that the expected number of byte where pushed. + if (push_size && (result.size() - script_size) != push_size) { + throw std::runtime_error( + "Hex number doesn't match the number of bytes being pushed"); + } + return result; } diff --git a/src/test/core_io_tests.cpp b/src/test/core_io_tests.cpp --- a/src/test/core_io_tests.cpp +++ b/src/test/core_io_tests.cpp @@ -6,6 +6,7 @@ #include "test/test_bitcoin.h" #include + #include BOOST_FIXTURE_TEST_SUITE(core_io_tests, BasicTestingSetup) @@ -24,4 +25,23 @@ } } +BOOST_AUTO_TEST_CASE(parse_push_test) { + BOOST_CHECK_NO_THROW(ParseScript("0x01 0x01")); + BOOST_CHECK_NO_THROW(ParseScript("0x01 XOR")); + BOOST_CHECK_NO_THROW(ParseScript("0x01 1")); + BOOST_CHECK_NO_THROW(ParseScript("0x01 ''")); + BOOST_CHECK_NO_THROW(ParseScript("0x02 0x0101")); + BOOST_CHECK_NO_THROW(ParseScript("0x02 42")); + BOOST_CHECK_NO_THROW(ParseScript("0x02 'a'")); + + BOOST_CHECK_THROW(ParseScript("0x01 0x0101"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x01 42"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 0x01"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 XOR"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 1"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 ''"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 0x010101"), std::runtime_error); + BOOST_CHECK_THROW(ParseScript("0x02 'ab'"), std::runtime_error); +} + BOOST_AUTO_TEST_SUITE_END() 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 @@ -17,8 +17,8 @@ [" 1 2 ", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], ["1", "", "P2SH,STRICTENC", "OK"], -["0x02 0x01 0x00", "", "P2SH,STRICTENC", "OK", "all bytes are significant, not only the last one"], -["0x09 0x00000000 0x00000000 0x10", "", "P2SH,STRICTENC", "OK", "equals zero when cast to Int64"], +["0x02 0x0100", "", "P2SH,STRICTENC", "OK", "all bytes are significant, not only the last one"], +["0x09 0x000000000000000010", "", "P2SH,STRICTENC", "OK", "equals zero when cast to Int64"], ["0x01 0x0b", "11 EQUAL", "P2SH,STRICTENC", "OK", "push 1 byte"], ["0x02 0x417a", "'Az' EQUAL", "P2SH,STRICTENC", "OK"], @@ -73,7 +73,7 @@ ["0", "IF RETURN ENDIF 1", "P2SH,STRICTENC", "OK", "RETURN only works if executed"], ["1 1", "VERIFY", "P2SH,STRICTENC", "OK"], -["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "P2SH,STRICTENC", "OK", "values >4 bytes can be cast to boolean"], +["1 0x05 0x0100000000", "VERIFY", "P2SH,STRICTENC", "OK", "values >4 bytes can be cast to boolean"], ["1 0x01 0x80", "IF 0 ENDIF", "P2SH,STRICTENC", "OK", "negative 0 is false"], ["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL", "P2SH,STRICTENC", "OK"], @@ -349,7 +349,7 @@ ["2147483647", "0x04 0xFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], ["2147483648", "0x05 0x0000008000 EQUAL", "P2SH,STRICTENC", "OK"], ["549755813887", "0x05 0xFFFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], -["549755813888", "0x06 0xFFFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["549755813888", "0x06 0x000000008000 EQUAL", "P2SH,STRICTENC", "OK"], ["9223372036854775807", "0x08 0xFFFFFFFFFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], ["-1", "0x01 0x81 EQUAL", "P2SH,STRICTENC", "OK", "Numbers are little-endian with the MSB being a sign bit"], ["-127", "0x01 0xFF EQUAL", "P2SH,STRICTENC", "OK"],