diff --git a/src/core_write.cpp b/src/core_write.cpp --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -34,7 +34,7 @@ while (it != script.end()) { CScript::const_iterator it2 = it; std::vector vch; - if (script.GetOp2(it, op, &vch)) { + if (script.GetOp(it, op, vch)) { if (op == OP_0) { ret += "0 "; continue; diff --git a/src/script/interpreter.h b/src/script/interpreter.h --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -93,4 +93,6 @@ uint32_t flags, const BaseSignatureChecker &checker, ScriptError *serror = nullptr); +int FindAndDelete(CScript &script, const CScript &b); + #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -44,13 +44,41 @@ stack.pop_back(); } +int FindAndDelete(CScript &script, const CScript &b) { + int nFound = 0; + if (b.empty()) { + return nFound; + } + + CScript result; + CScript::const_iterator pc = script.begin(), pc2 = script.begin(), + end = script.end(); + opcodetype opcode; + do { + result.insert(result.end(), pc2, pc); + while (static_cast(end - pc) >= b.size() && + std::equal(b.begin(), b.end(), pc)) { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } while (script.GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end); + script = std::move(result); + } + + return nFound; +} + static void CleanupScriptCode(CScript &scriptCode, const std::vector &vchSig, uint32_t flags) { // Drop the signature in scripts when SIGHASH_FORKID is not used. SigHashType sigHashType = GetHashType(vchSig); if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) || !sigHashType.hasForkId()) { - scriptCode.FindAndDelete(CScript(vchSig)); + FindAndDelete(scriptCode, CScript(vchSig)); } } diff --git a/src/script/script.h b/src/script/script.h --- a/src/script/script.h +++ b/src/script/script.h @@ -415,6 +415,10 @@ */ typedef prevector<28, uint8_t> CScriptBase; +bool GetScriptOp(CScriptBase::const_iterator &pc, + CScriptBase::const_iterator end, opcodetype &opcodeRet, + std::vector *pvchRet); + /** Serialized script, used inside transaction inputs and outputs */ class CScript : public CScriptBase { protected: @@ -509,82 +513,13 @@ return *this; } - bool GetOp(iterator &pc, opcodetype &opcodeRet, - std::vector &vchRet) { - // Wrapper so it can be called with either iterator or const_iterator. - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, &vchRet); - pc = begin() + (pc2 - begin()); - return fRet; - } - - bool GetOp(iterator &pc, opcodetype &opcodeRet) { - const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, nullptr); - pc = begin() + (pc2 - begin()); - return fRet; - } - bool GetOp(const_iterator &pc, opcodetype &opcodeRet, std::vector &vchRet) const { - return GetOp2(pc, opcodeRet, &vchRet); + return GetScriptOp(pc, end(), opcodeRet, &vchRet); } bool GetOp(const_iterator &pc, opcodetype &opcodeRet) const { - return GetOp2(pc, opcodeRet, nullptr); - } - - bool GetOp2(const_iterator &pc, opcodetype &opcodeRet, - std::vector *pvchRet) const { - opcodeRet = OP_INVALIDOPCODE; - if (pvchRet) { - pvchRet->clear(); - } - if (pc >= end()) { - return false; - } - - // Read instruction - if (end() - pc < 1) { - return false; - } - - uint32_t opcode = *pc++; - - // Immediate operand - if (opcode <= OP_PUSHDATA4) { - uint32_t nSize = 0; - if (opcode < OP_PUSHDATA1) { - nSize = opcode; - } else if (opcode == OP_PUSHDATA1) { - if (end() - pc < 1) { - return false; - } - nSize = *pc++; - } else if (opcode == OP_PUSHDATA2) { - if (end() - pc < 2) { - return false; - } - nSize = ReadLE16(&pc[0]); - pc += 2; - } else if (opcode == OP_PUSHDATA4) { - if (end() - pc < 4) { - return false; - } - nSize = ReadLE32(&pc[0]); - pc += 4; - } - if (end() - pc < 0 || uint32_t(end() - pc) < nSize) { - return false; - } - if (pvchRet) { - pvchRet->assign(pc, pc + nSize); - } - pc += nSize; - } - - opcodeRet = static_cast(opcode); - return true; + return GetScriptOp(pc, end(), opcodeRet, nullptr); } /** Encode/decode small integers: */ @@ -605,33 +540,6 @@ return (opcodetype)(OP_1 + n - 1); } - int FindAndDelete(const CScript &b) { - int nFound = 0; - if (b.empty()) { - return nFound; - } - - CScript result; - iterator pc = begin(), pc2 = begin(); - opcodetype opcode; - do { - result.insert(result.end(), pc2, pc); - while (size_t(end() - pc) >= b.size() && - std::equal(b.begin(), b.end(), pc)) { - pc = pc + b.size(); - ++nFound; - } - pc2 = pc; - } while (GetOp(pc, opcode)); - - if (nFound > 0) { - result.insert(result.end(), pc2, end()); - *this = result; - } - - return nFound; - } - /** * Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs as 20 sigops. With * pay-to-script-hash, that changed: CHECKMULTISIGs serialized in scriptSigs diff --git a/src/script/script.cpp b/src/script/script.cpp --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -508,3 +508,57 @@ bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); } + +bool GetScriptOp(CScriptBase::const_iterator &pc, + CScriptBase::const_iterator end, opcodetype &opcodeRet, + std::vector *pvchRet) { + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) { + pvchRet->clear(); + } + if (pc >= end) { + return false; + } + + // Read instruction + if (end - pc < 1) { + return false; + } + + uint32_t opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) { + uint32_t nSize = 0; + if (opcode < OP_PUSHDATA1) { + nSize = opcode; + } else if (opcode == OP_PUSHDATA1) { + if (end - pc < 1) { + return false; + } + nSize = *pc++; + } else if (opcode == OP_PUSHDATA2) { + if (end - pc < 2) { + return false; + } + nSize = ReadLE16(&pc[0]); + pc += 2; + } else if (opcode == OP_PUSHDATA4) { + if (end - pc < 4) { + return false; + } + nSize = ReadLE32(&pc[0]); + pc += 4; + } + if (end - pc < 0 || uint32_t(end - pc) < nSize) { + return false; + } + if (pvchRet) { + pvchRet->assign(pc, pc + nSize); + } + pc += nSize; + } + + opcodeRet = static_cast(opcode); + return true; +} 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 @@ -2880,45 +2880,45 @@ // delete nothing should be a no-op d = CScript(); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = CScript() << OP_1 << OP_2 << OP_3; d = CScript() << OP_2; expect = CScript() << OP_1 << OP_3; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3; d = CScript() << OP_3; expect = CScript() << OP_1 << OP_4; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4); BOOST_CHECK(s == expect); // PUSH 0x02ff03 onto stack s = ScriptFromHex("0302ff03"); d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); // PUSH 0x2ff03 PUSH 0x2ff03 s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("0302ff03"); expect = CScript(); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("02"); expect = s; // FindAndDelete matches entire opcodes - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("ff"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); // This is an odd edge case: strip of the push-three-bytes prefix, leaving @@ -2926,7 +2926,7 @@ s = ScriptFromHex("0302ff030302ff03"); d = ScriptFromHex("03"); expect = CScript() << ParseHex("ff03") << ParseHex("ff03"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Byte sequence that spans multiple opcodes: @@ -2935,40 +2935,40 @@ d = ScriptFromHex("feed51"); expect = s; // doesn't match 'inside' opcodes - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); // PUSH(0xfeed) OP_1 OP_VERIFY s = ScriptFromHex("02feed5169"); d = ScriptFromHex("02feed51"); expect = ScriptFromHex("69"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("feed51"); expect = s; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); BOOST_CHECK(s == expect); s = ScriptFromHex("516902feed5169"); d = ScriptFromHex("02feed51"); expect = ScriptFromHex("516969"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass expect = CScript() << OP_0 << OP_1; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1; d = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass expect = CScript() << OP_0 << OP_1; - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2); BOOST_CHECK(s == expect); // Another weird edge case: @@ -2977,13 +2977,13 @@ // ... can remove the invalid push d = ScriptFromHex("03feed"); expect = ScriptFromHex("00"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); s = ScriptFromHex("0003feed"); d = ScriptFromHex("00"); expect = ScriptFromHex("03feed"); - BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1); + BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1); BOOST_CHECK(s == expect); } diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -35,7 +35,7 @@ // In case concatenating two scripts ends up with two codeseparators, or an // extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR)); // Blank out other inputs' signatures for (auto &in : txTmp.vin) {