Changeset View
Changeset View
Standalone View
Standalone View
src/core_read.cpp
Show All 21 Lines | |||||
CScript ParseScript(const std::string &s) { | CScript ParseScript(const std::string &s) { | ||||
CScript result; | CScript result; | ||||
static std::map<std::string, opcodetype> mapOpNames; | static std::map<std::string, opcodetype> mapOpNames; | ||||
if (mapOpNames.empty()) { | if (mapOpNames.empty()) { | ||||
for (int op = 0; op < FIRST_UNDEFINED_OP_VALUE; op++) { | for (int op = 0; op < FIRST_UNDEFINED_OP_VALUE; op++) { | ||||
// ignore all "PUSHDATA" ops, but dont ignore OP_RESERVED | if (op < OP_PUSHDATA1) { | ||||
if (op < OP_NOP && op != OP_RESERVED) { | |||||
continue; | continue; | ||||
} | } | ||||
const char *name = GetOpName((opcodetype)op); | const char *name = GetOpName((opcodetype)op); | ||||
if (strcmp(name, "OP_UNKNOWN") == 0) { | if (strcmp(name, "OP_UNKNOWN") == 0) { | ||||
continue; | continue; | ||||
} | } | ||||
std::string strName(name); | std::string strName(name); | ||||
mapOpNames[strName] = (opcodetype)op; | mapOpNames[strName] = (opcodetype)op; | ||||
// Convenience: OP_ADD and just ADD are both recognized: | // Convenience: OP_ADD and just ADD are both recognized: | ||||
boost::algorithm::replace_first(strName, "OP_", ""); | boost::algorithm::replace_first(strName, "OP_", ""); | ||||
mapOpNames[strName] = (opcodetype)op; | mapOpNames[strName] = (opcodetype)op; | ||||
} | } | ||||
} | } | ||||
std::vector<std::string> words; | std::vector<std::string> words; | ||||
boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), | boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), | ||||
boost::algorithm::token_compress_on); | boost::algorithm::token_compress_on); | ||||
size_t push_size = 0, next_push_size = 0; | size_t push_size = 0, next_push_size = 0; | ||||
size_t script_size = 0; | size_t script_size = 0, size_change = 0; | ||||
// Deal with PUSHDATA1 operation with some more hacks. | |||||
size_t push_data_size = 0; | |||||
for (const auto &w : words) { | for (const auto &w : words) { | ||||
if (w.empty()) { | if (w.empty()) { | ||||
// Empty string, ignore. (boost::split given '' will return one | // Empty string, ignore. (boost::split given '' will return one | ||||
// word) | // word) | ||||
continue; | goto next; | ||||
} | |||||
// 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. | // Update script size. | ||||
script_size = result.size(); | script_size = result.size(); | ||||
// Make sure we keep track of the size of push operations. | // Make sure we keep track of the size of push operations. | ||||
push_size = next_push_size; | push_size = next_push_size; | ||||
next_push_size = 0; | |||||
// Decimal numbers | |||||
if (all(w, boost::algorithm::is_digit()) || | if (all(w, boost::algorithm::is_digit()) || | ||||
(boost::algorithm::starts_with(w, "-") && | (boost::algorithm::starts_with(w, "-") && | ||||
all(std::string(w.begin() + 1, w.end()), | all(std::string(w.begin() + 1, w.end()), | ||||
boost::algorithm::is_digit()))) { | boost::algorithm::is_digit()))) { | ||||
// Number | // Number | ||||
int64_t n = atoi64(w); | int64_t n = atoi64(w); | ||||
result << n; | result << n; | ||||
continue; | goto next; | ||||
} | } | ||||
// Hex Data | |||||
if (boost::algorithm::starts_with(w, "0x") && | if (boost::algorithm::starts_with(w, "0x") && | ||||
(w.begin() + 2 != w.end())) { | (w.begin() + 2 != w.end())) { | ||||
if (!IsHex(std::string(w.begin() + 2, w.end()))) { | if (!IsHex(std::string(w.begin() + 2, w.end()))) { | ||||
// Should only arrive here for improperly formatted hex values | // Should only arrive here for improperly formatted hex values | ||||
throw std::runtime_error("Hex numbers expected to be formatted " | throw std::runtime_error("Hex numbers expected to be formatted " | ||||
"in full-byte chunks (ex: 0x00 " | "in full-byte chunks (ex: 0x00 " | ||||
"instead of 0x0)"); | "instead of 0x0)"); | ||||
} | } | ||||
// Raw hex data, inserted NOT pushed onto stack: | // Raw hex data, inserted NOT pushed onto stack: | ||||
std::vector<uint8_t> raw = | std::vector<uint8_t> raw = | ||||
ParseHex(std::string(w.begin() + 2, w.end())); | 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 " | // TODO: Disallow `push_size == 0 && raw.size() > 1` here, tests | ||||
"number of bytes being pushed"); | // should not have multibyte raw opcode data. However some of | ||||
} | // them currently do. | ||||
// 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()); | result.insert(result.end(), raw.begin(), raw.end()); | ||||
continue; | goto next; | ||||
} | } | ||||
if (w.size() >= 2 && boost::algorithm::starts_with(w, "'") && | if (w.size() >= 2 && boost::algorithm::starts_with(w, "'") && | ||||
boost::algorithm::ends_with(w, "'")) { | boost::algorithm::ends_with(w, "'")) { | ||||
// Single-quoted string, pushed as data. NOTE: this is poor-man's | // Single-quoted string, pushed as data. NOTE: this is poor-man's | ||||
// parsing, spaces/tabs/newlines in single-quoted strings won't | // parsing, spaces/tabs/newlines in single-quoted strings won't | ||||
// work. | // work. | ||||
std::vector<uint8_t> value(w.begin() + 1, w.end() - 1); | std::vector<uint8_t> value(w.begin() + 1, w.end() - 1); | ||||
result << value; | result << value; | ||||
continue; | goto next; | ||||
} | } | ||||
if (mapOpNames.count(w)) { | if (mapOpNames.count(w)) { | ||||
// opcode, e.g. OP_ADD or ADD: | // opcode, e.g. OP_ADD or ADD: | ||||
opcodetype op = mapOpNames[w]; | opcodetype op = mapOpNames[w]; | ||||
result << op; | |||||
goto next; | |||||
} | |||||
throw std::runtime_error("Error parsing script: " + s); | |||||
next: | |||||
next_push_size = 0; | |||||
size_change = result.size() - script_size; | |||||
if (push_size && size_change != push_size) { | |||||
throw std::runtime_error("Wrong number of bytes being pushed."); | |||||
} | |||||
// Push data size is not a CScriptNum (Because it is | |||||
// 2's-complement instead of 1's complement). We need to use | |||||
// ReadLE(N) instead of converting to a CScriptNum. | |||||
if (push_size == 0 && size_change == 1) { | |||||
opcodetype op = opcodetype(*result.rbegin()); | |||||
// If we have what looks like an immediate push, figure out its | |||||
// size. | |||||
if (op < OP_PUSHDATA1) { | |||||
next_push_size = op; | |||||
continue; | |||||
} | |||||
switch (op) { | switch (op) { | ||||
case OP_PUSHDATA1: | case OP_PUSHDATA1: | ||||
next_push_size = 1; | push_data_size = next_push_size = 1; | ||||
break; | break; | ||||
case OP_PUSHDATA2: | case OP_PUSHDATA2: | ||||
next_push_size = 2; | push_data_size = next_push_size = 2; | ||||
break; | break; | ||||
case OP_PUSHDATA4: | case OP_PUSHDATA4: | ||||
next_push_size = 4; | push_data_size = next_push_size = 4; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
result << op; | |||||
continue; | |||||
} | } | ||||
throw std::runtime_error("Error parsing script: " + s); | if (push_size != 0) { | ||||
auto offset = &result[script_size]; | |||||
assert(size_change >= push_size); | |||||
if (push_data_size == 1) { | |||||
next_push_size = *offset; | |||||
} else if (push_data_size == 2) { | |||||
next_push_size = ReadLE16(offset); | |||||
} else if (push_data_size == 4) { | |||||
next_push_size = ReadLE32(offset); | |||||
} | } | ||||
// Check that the expected number of byte where pushed. | push_data_size = 0; | ||||
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; | return result; | ||||
} | } | ||||
bool DecodeHexTx(CMutableTransaction &tx, const std::string &strHexTx) { | bool DecodeHexTx(CMutableTransaction &tx, const std::string &strHexTx) { | ||||
if (!IsHex(strHexTx)) { | if (!IsHex(strHexTx)) { | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 68 Lines • Show Last 20 Lines |